Отражение Java: как получить значение поля от объекта, не зная его класса
Скажем, у меня есть метод, который возвращает пользовательский List
с некоторыми объектами. Они возвращаются мне как Object
. Мне нужно получить значение определенного поля из этих объектов, но я не знаю класс объектов.
Есть ли способ сделать это через Reflecion или как-нибудь еще?
Ответы
Ответ 1
Предполагая простой случай, когда ваше поле public
:
List list; // from your method
for(Object x : list) {
Class<?> clazz = x.getClass();
Field field = clazz.getField("fieldName"); //Note, this can throw an exception if the field doesn't exist.
Object fieldValue = field.get(x);
}
Но это довольно уродливо, и я упустил все попытки уловов и сделал ряд предположений (открытое поле, отражение доступно, хороший менеджер безопасности).
Если вы можете изменить свой метод, чтобы вернуть List<Foo>
, это становится очень простым, потому что итератор может дать вам информацию о типе:
List<Foo> list; //From your method
for(Foo foo:list) {
Object fieldValue = foo.fieldName;
}
Или, если вы потребляете интерфейс Java 1.4, где дженерики недоступны, но вы знаете тип объектов, которые должны быть в списке...
List list;
for(Object x: list) {
if( x instanceof Foo) {
Object fieldValue = ((Foo)x).fieldName;
}
}
Не требуется отражения:)
Ответ 2
Если вы знаете, в каком классе находится поле, вы можете получить к нему доступ с помощью отражения. Этот пример (он в Groovy, но вызовы метода идентичны) получает объект Field для класса Foo и получает его значение для объекта b
. Это показывает, что вам не нужно заботиться о конкретном конкретном классе объекта, важно то, что вы знаете класс, в котором находится поле, и что этот класс является либо конкретным классом, либо суперклассом объекта.
groovy:000> class Foo { def stuff = "asdf"}
===> true
groovy:000> class Bar extends Foo {}
===> true
groovy:000> b = new Bar()
===> [email protected]
groovy:000> f = Foo.class.getDeclaredField('stuff')
===> private java.lang.Object Foo.stuff
groovy:000> f.getClass()
===> class java.lang.reflect.Field
groovy:000> f.setAccessible(true)
===> null
groovy:000> f.get(b)
===> asdf
Ответ 3
Я настоятельно рекомендую использовать обобщенные методы Java, чтобы указать, какой тип объекта находится в этом списке, т.е. List<Car>
. Если у вас есть автомобили и грузовики, вы можете использовать обычный суперкласс/интерфейс, подобный этому List<Vehicle>
.
Однако вы можете использовать Spring ReflectionUtils, чтобы сделать поля доступными, даже если они закрыты, как приведенный ниже пример:
List<Object> list = new ArrayList<Object>();
list.add("some value");
list.add(3);
for(Object obj : list)
{
Class<?> clazz = obj.getClass();
Field field = org.springframework.util.ReflectionUtils.findField(clazz, "value");
org.springframework.util.ReflectionUtils.makeAccessible(field);
System.out.println("value=" + field.get(obj));
}
Запуск этого файла имеет выход:
value = [C @1b67f74
value = 3
Ответ 4
public abstract class Refl {
/** Use: Refl.<TargetClass>get(myObject,"x.y[0].z"); */
public static<T> T get(Object obj, String fieldPath) {
return (T) getValue(obj, fieldPath);
}
public static Object getValue(Object obj, String fieldPath) {
String[] fieldNames = fieldPath.split("[\\.\\[\\]]");
String success = "";
Object res = obj;
for (String fieldName : fieldNames) {
if (fieldName.isEmpty()) continue;
int index = toIndex(fieldName);
if (index >= 0) {
try {
res = ((Object[])res)[index];
} catch (ClassCastException cce) {
throw new RuntimeException("cannot cast "+res.getClass()+" object "+res+" to array, path:"+success, cce);
} catch (IndexOutOfBoundsException iobe) {
throw new RuntimeException("bad index "+index+", array size "+((Object[])res).length +" object "+res +", path:"+success, iobe);
}
} else {
Field field = getField(res.getClass(), fieldName);
field.setAccessible(true);
try {
res = field.get(res);
} catch (Exception ee) {
throw new RuntimeException("cannot get value of ["+fieldName+"] from "+res.getClass()+" object "+res +", path:"+success, ee);
}
}
success += fieldName + ".";
}
return res;
}
public static Field getField(Class<?> clazz, String fieldName) {
Class<?> tmpClass = clazz;
do {
try {
Field f = tmpClass.getDeclaredField(fieldName);
return f;
} catch (NoSuchFieldException e) {
tmpClass = tmpClass.getSuperclass();
}
} while (tmpClass != null);
throw new RuntimeException("Field '" + fieldName + "' not found in class " + clazz);
}
private static int toIndex(String s) {
int res = -1;
if (s != null && s.length() > 0 && Character.isDigit(s.charAt(0))) {
try {
res = Integer.parseInt(s);
if (res < 0) {
res = -1;
}
} catch (Throwable t) {
res = -1;
}
}
return res;
}
}
Он поддерживает выборку полей и элементов массива, например:
System.out.println(""+Refl.getValue(b,"x.q[0].z.y"));
нет разницы между точками и фигурными скобками, они просто разделители, а пустые имена полей игнорируются:
System.out.println(""+Refl.getValue(b,"x.q[0].z.y[value]"));
System.out.println(""+Refl.getValue(b,"x.q.1.y.z.value"));
System.out.println(""+Refl.getValue(b,"x[q.1]y]z[value"));
Ответ 5
Есть еще один способ, у меня такая же ситуация в моем проекте.
я решил этот путь
List<Object[]> list = HQL.list();
В вышеперечисленном языке запросов на спящий режим я знаю, в каком месте находятся мои объекты, поэтому я сделал это:
for(Object[] obj : list){
String val = String.valueOf(obj[1]);
int code =Integer.parseint(String.valueof(obj[0]));
}
таким образом вы можете легко получить смешанные объекты, но вы должны заранее знать, в каком месте, какое значение вы получаете или можете просто проверить, распечатав значения, которые нужно знать.
извините за плохой английский
Я надеюсь, что эта помощь