Java reflection: how to get field value from an object, not knowing its class

前端 未结 5 1101
无人及你
无人及你 2020-11-29 04:10

Say, I have a method that returns a custom List with some objects. They are returned as Object to me. I need to get value of a certain field from t

相关标签:
5条回答
  • 2020-11-29 04:20

    If you know what class the field is on you can access it using reflection. This example (it's in Groovy but the method calls are identical) gets a Field object for the class Foo and gets its value for the object b. It shows that you don't have to care about the exact concrete class of the object, what matters is that you know the class the field is on and that that class is either the concrete class or a superclass of the object.

    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
    
    0 讨论(0)
  • 2020-11-29 04:28

    Assuming a simple case, where your field is 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);
    }
    

    But this is pretty ugly, and I left out all of the try-catches, and makes a number of assumptions (public field, reflection available, nice security manager).

    If you can change your method to return a List<Foo>, this becomes very easy because the iterator then can give you type information:

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

    Or if you're consuming a Java 1.4 interface where generics aren't available, but you know the type of the objects that should be in the list...

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

    No reflection needed :)

    0 讨论(0)
  • 2020-11-29 04:29

    I strongly recommend using Java generics to specify what type of object is in that List, ie. List<Car>. If you have Cars and Trucks you can use a common superclass/interface like this List<Vehicle>.

    However, you can use Spring's ReflectionUtils to make fields accessible, even if they are private like the below runnable example:

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

    Running this has an output of:

    value=[C@1b67f74
    value=3

    0 讨论(0)
  • 2020-11-29 04:36

    There is one more way, i got the same situation in my project. i solved this way

    List<Object[]> list = HQL.list();

    In above hibernate query language i know at which place what are my objects so what i did is :

    for(Object[] obj : list){
    String val = String.valueOf(obj[1]);
    int code =Integer.parseint(String.valueof(obj[0]));
    }
    

    this way you can get the mixed objects with ease, but you should know in advance at which place what value you are getting or you can just check by printing the values to know. sorry for the bad english I hope this help

    0 讨论(0)
  • 2020-11-29 04:42
    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;
        }
    }
    

    It supports fetching fields and array items, e.g.:

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

    there is no difference between dots and braces, they are just delimiters, and empty field names are ignored:

    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"));
    
    0 讨论(0)
提交回复
热议问题