在開發(fā)中,經(jīng)常要存在需要將數(shù)據(jù)收集過來然后在內(nèi)存中進(jìn)行過濾,排序,分頁的情況。寫了一個過濾,排序,分頁的工具,結(jié)果發(fā)現(xiàn)有一些問題,下面來說下這個問題。
一、問題說明
先看下面一段代碼。排序時,自定義了一個比較器,通常情況下沒有問題。
但是,要是String paramType = o1.getClass().getDeclaredField(paramName).getGenericType().toString();語句中o1為一個繼承類,paramName為一個父類的成員變量,就會報異常:java.lang.NoSuchFieldException:XXX
Collections.sort(originList, new ParamComparator<T>(orderBy, order));
public int compare1(T o1, T o2) {
if (paramName == null || paramName.isEmpty() || o1 == o2) {
return 0;
}
String upperParamType = paramName.substring(0, 1).toUpperCase() + paramName.substring(1);
try {
String paramType = o1.getClass().getDeclaredField(paramName).getGenericType().toString();
Method m = o1.getClass().getMethod("get" + upperParamType);
int result;
if (paramType.equals("class java.lang.String")) {
String valueParam1 = (String) m.invoke(o1);
String valueParam2 = (String) m.invoke(o2);
if ((result = value(valueParam1, valueParam2)) != 0) {
return result;
} else {
return isAscend ? valueParam1.compareTo(valueParam2) : valueParam2.compareTo(valueParam1);
}
} else if (paramType.equals("class java.util.Date")) {
Date valueParam1 = (Date) m.invoke(o1);
Date valueParam2 = (Date) m.invoke(o2);
if ((result = value(valueParam1, valueParam2)) != 0) {
return result;
} else {
return isAscend ? valueParam1.compareTo(valueParam2) : valueParam2.compareTo(valueParam1);
}
} else {
return 0;
}
} catch (Exception ex) {
log.warn("ParamComparator.compare Exception!");
log.warn("paramName : '" + paramName + "', isAscend : " + isAscend + ", o1 : " + o1 + ", o2 : " + o2);
ex.printStackTrace();
return 0;
}
}
二、問題定位:
JAVA 反射獲取不到父類中聲明的字段,但可以獲取到父類中聲明的方法。這樣可以獲取到父類中該字段的值。
三、問題解決
下面說一種解決方案,就是通過getSuperclass方法獲取父類再調(diào)用getDeclaredField方法
public int compare(T o1, T o2) {
if (paramName == null || paramName.isEmpty() || o1 == o2) {
return 0;
}
String upperParamType = paramName.substring(0, 1).toUpperCase() + paramName.substring(1);
try {
// String paramType = o1.getClass().getDeclaredField(paramName).getGenericType().toString();
String paramType = getFiled(paramName, o1.getClass()).getGenericType().toString();
Method m = o1.getClass().getMethod("get" + upperParamType);
int result;
if (paramType.equals("class java.lang.String")) {
String valueParam1 = (String) m.invoke(o1);
String valueParam2 = (String) m.invoke(o2);
if ((result = value(valueParam1, valueParam2)) != 0) {
return result;
} else {
return isAscend ? valueParam1.compareTo(valueParam2) : valueParam2.compareTo(valueParam1);
}
} else if (paramType.equals("class java.util.Date")) {
Date valueParam1 = (Date) m.invoke(o1);
Date valueParam2 = (Date) m.invoke(o2);
if ((result = value(valueParam1, valueParam2)) != 0) {
return result;
} else {
return isAscend ? valueParam1.compareTo(valueParam2) : valueParam2.compareTo(valueParam1);
}
} else {
return 0;
}
} catch (Exception ex) {
log.warn("ParamComparator.compare Exception!");
log.warn("paramName : '" + paramName + "', isAscend : " + isAscend + ", o1 : " + o1 + ", o2 : " + o2);
ex.printStackTrace();
return 0;
}
}
public static Field getFiled(String tar, Class clazz) {
String error = null;
Field field = null;
while (clazz != null) {
try {
field = clazz.getDeclaredField(tar);
error = null;
break;
} catch (Exception e) {
clazz = clazz.getSuperclass();
error = e.getMessage();
}
}
if (error != null || field == null) {
throw new RuntimeException("無法獲取源字段:" + tar);
}
return field;
}
四、知識點:
- getField vs getDeclaredField
getField用于返回一個指定名稱的屬性,但是這個屬性必須是公有的,這個屬性可以在父類中定義。如果是私有屬性或者是保護(hù)屬性,那么都會拋出異常提示找不到這個屬性; getDeclaredField獲得在這個類型的聲明中定義的指定名稱的屬性,這個屬性必須是在這個類型的聲明中定義,但可以使私有和保護(hù)的。
五、參考
【1】java反射獲取父類和子類字段值、賦值
【2】JAVA 反射獲取不到父類中聲明的字段,但可以獲取到父類中聲明的方法