如何使用反射更新层次结构中的成员



我有一个名为沃尔沃的类,它继承自汽车。
它包含一个名为 Engine 的类,其中包含 Valve。

public class Engine {
    public Engine(int e, int f) {
        this.e = e;
        valve = new Valve(f);
    }
    private int e;
    private Valve valve;
}

阀门有一个数据成员 - int f;

public class Valve {
    public Valve(int f) {
        this.f = f;
    }
    public int f;
}

一切都是私有的,不包含二传手。

我想使用以下字符串使用反射设置一个值

fillIn(volvo, "Engine.Valve.f", 10);

使用此代码:

String[] splits = path.split("\.");
Class tmpClass = obj.getClass();
for (int i = 0; i < splits.length; i++) {
    if (i + 1 != splits.length) {
        Field field = tmpClass.getDeclaredField(splits[i]);
        tmpClass = field.getClass();
    } else {
        System.out.println("**** - " + splits[i]);
    }
}

问题:
1. 我的对象是沃尔沃。发动机位于汽车。我可以在沃尔沃迭代时获得字段吗?
2. 如何设置值?每次的类型都不同。
3. 有没有更好的做法(FW?)可以做到这一点?

谢谢。

要访问私有字段,您可以像这样设置对字段true的访问权限:

field.setAccessible(true);

请看这个例子:

http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html和Javadoc
http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible(布尔值)


顺便说一句,使用二传手肯定会更好
编辑:
您需要存储要设置的字段的对象:
经过一些编辑的代码,由javadoc编写,未经测试
String[] splits = path.split("\.");
Class tmpClass = obj.getClass();
Object tmpObject = obj;
for (int i = 0; i < splits.length; i++) {
    if (i + 1 != splits.length) {
        Field field = tmpClass.getDeclaredField(splits[i]);
        tmpClass = field.getClass();
        if(tmpClass.equals(Integer.class)){
            field.setInt(tmpObject,42); //where 42 is the number to set
            return;
        }
        tmpObject = field.get(tmpObject); // tmpObject contais the object to set
    }
}

也许代码不起作用,但我希望它能说明这个想法

我解决了它,而无需访问级别或getters和setters使用。
(这需要为生产重写+目前只能解决一个整数)

private static void fillIn(Object obj, String path, int value) {
        Object tempObj = obj;
        ReflectionData reflectionData;
        if (path.contains(".")) {
            String[] splits = path.split("\.");
            for (int i = 0; i < splits.length; i++) {
                reflectionData = getReflectionData(tempObj, splits[i]);
                if (i + 1 != splits.length) {
                    tempObj = reflectionData.getFieldObject();
                } else {
                    reflectionData.setFieldObject(value);
                }
            }
        }
    }
    private static ReflectionData getReflectionData(Object obj, String fieldName) {
        ReflectionData reflectionData = null;
        Class tmpClass = obj.getClass();
        while (tmpClass != null) {
            Field[] declaredFields = tmpClass.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.getName().equals(fieldName)){
                    reflectionData = new ReflectionData(tmpClass, field, obj);
                }
            }
            tmpClass = tmpClass.getSuperclass();
        }
        return reflectionData;
    }

哪里

公共类反射数据 {

public ReflectionData(Class<?> aClass, Field field, Object object) {
    this.aClass = aClass;
    this.field = field;
    this.object = object;
}
public Class<?> aClass;
public Field field;
public Object object;
public Object getFieldObject() {
    Object o = null;
    try {
        o = field.get(object);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return o;
}
public void setFieldObject(int value) {
    try {
        field.set(object, value);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

}

您正在朝着正确的方向前进,但是您的代码中存在几个问题:

  1. 正如您已经发现的那样,如果在您调用它的类中声明了一个字段,而不是在超类中声明,getDeclaredField将为您获取一个字段。但是,您可以使用如果字段未在当前类中声明,则会抛出NoSuchFieldException以获取超类并重试。
  2. 尝试通过反射获取字段时,传递给 getDeclaredField 方法的值区分大小写。这意味着您需要调用 fillIn 方法作为fillIn(volvo, "engine.valve.f", 10);,因为沃尔沃类(通过继承 Car 类)具有一个名为 engine 的字段,而不是 Engine
  3. tmpClass = field.getClass();将检索field对象的类,这当然是Field 。您要做的是检索由 field 表示的声明字段的类型,这可以使用 field.getType() 来完成。

因此,您的代码将变为:

private void fillIn(Object obj, String path, int number) throws Exception {
    String[] splits = path.split("\.");
    Class tmpClass = obj.getClass();
    for (int i = 0; i < splits.length; i++) {
        if (i + 1 != splits.length) {
            Field field = null;
            while ((field == null) && (tmpClass != null)) {
                try {
                    field = tmpClass.getDeclaredField(splits[i]);
                } catch (NoSuchFieldException ex) {
                    tmpClass = tmpClass.getSuperclass();
                }
            }
            tmpClass = field.getType();
        } else {
            System.out.println("**** - " + splits[i]);
        }
    }
}

一个替代答案,显示如果使用 getter 和 setter 是多么容易。

// Assuming that the Volvo class builds it own engine,
// with the Engine class building its own value.
Car volvo = new Volvo();
int newF = 10;
volvo.getEngine().getValve().setF(newF); // Done

最新更新