web-dev-qa-db-fra.com

Java réflexion: comment obtenir la valeur d'un champ à partir d'un objet sans connaître sa classe

Dis, j'ai une méthode qui retourne un List personnalisé avec quelques objets. Ils me sont retournés sous la forme Object. Je dois obtenir la valeur d'un certain champ à partir de ces objets, mais je ne connais pas la classe des objets.

Y a-t-il un moyen de le faire via Reflecion ou d'une autre manière?

29
svz

En supposant un cas simple, où votre champ est 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);
}

Mais c’est plutôt moche, j’ai laissé de côté toutes les prises d’essai et fait un certain nombre d’hypothèses (champ public, réflexion disponible, gestionnaire de la sécurité de Nice).

Si vous pouvez changer votre méthode pour retourner un List<Foo>, cela devient très facile car l’itérateur peut alors vous donner des informations de type:

List<Foo> list; //From your method
for(Foo foo:list) {
    Object fieldValue = foo.fieldName;
}

Ou si vous utilisez une interface Java 1.4 où les génériques ne sont pas disponibles, mais que vous connaissez le type des objets qui devraient figurer dans la liste ...

List list;
for(Object x: list) {
   if( x instanceof Foo) {
      Object fieldValue = ((Foo)x).fieldName;
   }
}

Aucune réflexion nécessaire :)

44
Charlie

Si vous connaissez la classe du champ, vous pouvez y accéder en utilisant la réflexion. Cet exemple (il est dans Groovy mais les appels de méthode sont identiques) obtient un objet Field pour la classe Foo et obtient sa valeur pour l'objet b. Cela montre que vous n'avez pas à vous soucier de la classe concrète exacte de l'objet. Ce qui compte, c'est de connaître la classe sur laquelle se trouve le champ et que cette classe est la classe concrète ou une superclasse de l'objet.

groovy:000> class Foo { def stuff = "asdf"}
===> true
groovy:000> class Bar extends Foo {}
===> true
groovy:000> b = new Bar()
===> Bar@1f2be27
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
4
Nathan Hughes
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;
    }
}

Il prend en charge la récupération des champs et des éléments de tableau, par exemple:

System.out.println(""+Refl.getValue(b,"x.q[0].z.y"));

il n'y a pas de différence entre les points et les accolades, ce ne sont que des délimiteurs et les noms de champs vides sont ignorés:

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"));
1

Je recommande fortement d'utiliser Java génériques pour spécifier le type d'objet figurant dans cette liste, c'est-à-dire. List<Car>. Si vous avez Cars and Trucks, vous pouvez utiliser une superclasse/interface commune comme celle-ci List<Vehicle>.

Cependant, vous pouvez utiliser les commandes ReflectionUtils de Spring pour rendre les champs accessibles, même s'ils sont privés, comme dans l'exemple ci-dessous:

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));
}

En cours d'exécution, cela produit:

valeur = [C @ 1b67f74
valeur = 3

1
Erich