我一直在玩Java中的反思......我有点困惑。
我希望下面的程序允许我更改类中公共成员变量的值。但是,我收到IllegalArgumentException。有什么想法吗?
public class ColinTest {
public String msg = "fail";
public ColinTest() { }
public static void main(String args[]) throws Exception {
ColinTest test = new ColinTest();
Class c = test.getClass();
Field[] decfields = c.getDeclaredFields();
decfields[0].set("msg", "success");
System.out.println(ColinTest.msg)
}
}
我收到此消息 -
Exception in thread "main" java.lang.IllegalArgumentException
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:57)
at java.lang.reflect.Field.set(Field.java:656)
at ColinTest.main(ColinTest.java:44)
感谢。
答案 0 :(得分:8)
Field.set
方法的第一个参数应该是您要反思的对象。
decfields[0].set("msg", "success");
应阅读:
decfields[0].set(test, "success");
此外,最后的System.out.println
调用应引用test
对象而不是类ColinTest
,因为我认为其目的是输出test.msg
的内容字段。
<强>更新强>
正如toolkit和Chris所指出的,Class.getDeclaredField
方法可用于指定字段的名称以便检索它:
Field msgField = test.getClass().getDeclaredField("msg");
// or alternatively:
Field msgField = ColinTest.class.getDeclaredField("msg");
然后,set
的{{1}}方法可以调用为:
msgField
正如工具包已经指出的那样,这种方式有其好处,如果对象中添加了更多字段,msgField.set(test, "success");
返回的字段顺序可能不一定返回字段{{1} }作为数组的第一个元素。根据返回的数组的顺序,某种方式可能会在对类进行更改时导致问题。
因此,使用Class.getDeclaredFields
并声明所需字段的名称可能是更好的主意。
答案 1 :(得分:2)
set()的第一个arg应该是你正在改变其字段的对象...即test。
答案 2 :(得分:2)
请确保您发布的代码实际编译(您需要test.msg
,而不是ColinTest.msg
)。
您也可以考虑使用较新版本的Java,它可以提供更具体的错误消息:
% java ColinTest
Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.String field ColinTest.msg to java.lang.String
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:57)
at java.lang.reflect.Field.set(Field.java:657)
at ColinTest.main(ColinTest.java:13)
这可能会引导您找到其他人发布的解决方案。
答案 3 :(得分:1)
当您调用getDeclaredFields
时,每个数组元素将包含一个Field
对象,该对象表示类中的字段,而不是实例化对象上的字段。
这就是为什么在使用setter时必须指定要设置该字段的对象:
ColinTest test = new ColinTest();
Field msgfield = ColinTest.class.getDeclaredField("msg");
msgField.set(test, "success");
答案 4 :(得分:1)
你想要的是:
Field msgField = c.getDeclaredField("msg");
msgField.set(test, "Success");
请谨慎使用decfields[0]
,因为在您向班级添加第二个字段时可能无法获得预期效果(您没有检查decfields[0]
是否与msg
字段对应)
答案 5 :(得分:0)
我偶然发现了这个页面的内容,因为奇怪的是,我无法在班级中设置公共字符串字段。代码将在每个for循环中的ArrayList中添加新行。问题是,我把新对象的实例化代码(使用反射)只在内部之外进行一次。
private ArrayList processDataSetResultSetAsArrayList(ResultSet resultSet, String fqnModel) {
ArrayList result = new ArrayList();
try {
ResultSetMetaData metaData;
int nColoumn;
String columnName;
String fieldValue;
Field field;
Object modelInstance;
metaData = resultSet.getMetaData();
nColoumn = metaData.getColumnCount();
resultSet.beforeFirst();
Class modelClass = Class.forName(fqnModel);
while (resultSet.next()) {
modelInstance = modelClass.newInstance();
for (int i = 1; i <= nColoumn; i++) {
columnName = metaData.getColumnName(i);
field = modelInstance.getClass().getDeclaredField(columnName);
fieldValue = resultSet.getString(i);
field.set(modelInstance, fieldValue);
}
result.add(modelInstance);
}
} catch (Exception ex) {
Logger.getLogger(DB.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
检查现在我将Class.forName(fqnModel)移到while循环之外。因为我们当然只需要创建一次Class对象。但是,在每个for循环之前,我创建了一个最终将添加到ArrayList中的模型对象。
要清楚,这是我的BiroModel类看起来像:
public class BiroModel extends Model {
public String idbiro = "";
public String biro = "";
public BiroModel() {
}
public BiroModel(String table, String pkField) {
super(table, pkField);
fqn = BiroModel.class.getName();
}
public String getBiro() {
return biro;
}
public void setBiro(String biro) {
this.biro = biro;
}
public String getIdbiro() {
return idbiro;
}
public void setIdbiro(String idbiro) {
this.idbiro = idbiro;
}
}
我在这里创建一个约定,所有字段对象都应该声明为public。但是,因为我需要EL语法,我仍然需要为这个公共字段创建getter / setter。