我有一个名为沃尔沃的类,它继承自汽车。
它包含一个名为 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();
}
}
}
您正在朝着正确的方向前进,但是您的代码中存在几个问题:
- 正如您已经发现的那样,如果在您调用它的类中声明了一个字段,而不是在超类中声明,
getDeclaredField
将为您获取一个字段。但是,您可以使用如果字段未在当前类中声明,则会抛出NoSuchFieldException
以获取超类并重试。 - 尝试通过反射获取字段时,传递给
getDeclaredField
方法的值区分大小写。这意味着您需要调用fillIn
方法作为fillIn(volvo, "engine.valve.f", 10);
,因为沃尔沃类(通过继承 Car 类)具有一个名为engine
的字段,而不是Engine
。 -
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