java 设置公共成员时的 IllegalArgumentException
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/919962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
IllegalArgumentException when setting public member
提问by colinjwebb
I've been playing around with reflection in Java... and I'm a little bit baffled.
我一直在玩 Java 中的反射......我有点困惑。
I was hoping that the program below would allow me to change the value of a public member variable within a class. However, I receive an IllegalArgumentException. Any ideas?
我希望下面的程序允许我更改类中公共成员变量的值。但是,我收到了一个 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)
}
}
I receive this message -
我收到这条消息——
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)
Thanks.
谢谢。
回答by coobird
The first argument of the Field.setmethod should be the object which you are reflecting on.
该Field.set方法的第一个参数应该是您正在思考的对象。
decfields[0].set("msg", "success");
Should read:
应该读:
decfields[0].set(test, "success");
Furthermore, the final System.out.printlncall should refer to the testobject rather than the class ColinTest, as I presume the intention is to output the contents of the test.msgfield.
此外,最终System.out.println调用应该引用testobject 而不是 class ColinTest,因为我认为目的是输出test.msg字段的内容。
Update
更新
As pointed out by toolkitand Chris, the Class.getDeclaredFieldmethod can be used to specify the name of the field in order to retrieve it:
正如toolkit和Chris所指出的,该Class.getDeclaredField方法可用于指定字段的名称以检索它:
Field msgField = test.getClass().getDeclaredField("msg");
// or alternatively:
Field msgField = ColinTest.class.getDeclaredField("msg");
Then, the setmethod of the msgFieldcan be invoked as:
然后,可以调用的set方法msgField为:
msgField.set(test, "success");
This way has its benefit, as already pointed out by toolkit, if there are more fields added to the object, the order of the fields that are returned by Class.getDeclaredFieldsmay not necessarily return the field msgas the first element of the array. Depending on the order of the returned array to be a certain way may cause problems when changes are made to the class.
这种方式有它的好处,正如工具包已经指出的那样,如果向对象添加了更多字段,则返回的字段的顺序Class.getDeclaredFields可能不一定将字段msg作为数组的第一个元素返回。根据返回数组的顺序,当对类进行更改时,某种方式可能会导致问题。
Therefore, it would probably be a better idea to use getDeclaredFieldand declare the name of the desired field.
因此,使用getDeclaredField和声明所需字段的名称可能是一个更好的主意。
回答by CurtainDog
The first arg to set() should be the object whose field you are changing... namely test.
set() 的第一个 arg 应该是您要更改其字段的对象......即测试。
回答by Nicholas Riley
Please make sure the code you post actually compiles (you want test.msg, not ColinTest.msg).
请确保您发布的代码实际编译(您想要test.msg,而不是ColinTest.msg)。
You may also consider using a newer version of Java, which could provide a more specific error message:
您还可以考虑使用较新版本的 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)
which would probably have led you to the solution others have posted.
这可能会让您找到其他人发布的解决方案。
回答by Christian Hang-Hicks
When you are calling getDeclaredFields, each array element will contain a Fieldobject that represents a field on the class, no on an an instantiated object.
当您调用 时getDeclaredFields,每个数组元素将包含一个Field对象,该对象表示该类中的一个字段,而不是一个实例化对象。
That's why you have to specify the object on which you want to set that field, when using the setter:
这就是为什么在使用 setter 时必须指定要在其上设置该字段的对象的原因:
ColinTest test = new ColinTest();
Field msgfield = ColinTest.class.getDeclaredField("msg");
msgField.set(test, "success");
回答by toolkit
What you want is:
你想要的是:
Field msgField = c.getDeclaredField("msg");
msgField.set(test, "Success");
Take care using decfields[0]since you may get not get what you expected when you add a second field to your class (you are not checking that decfields[0]corresponds to the msgfield)
请小心使用,decfields[0]因为当您向类中添加第二个字段时,您可能得不到预期的结果(您没有检查decfields[0]与该msg字段对应的字段)
回答by swdev
I stumbled accros this page, because weirdly, I can not sets public string field in my class. The code will add new row in ArrayList in each for loop. The problem is, I put instantiation code of new object (using reflection that is) only once, outside the inner for.
我偶然发现了这个页面,因为奇怪的是,我无法在我的班级中设置公共字符串字段。该代码将在每个 for 循环的 ArrayList 中添加新行。问题是,我只将新对象的实例化代码(使用反射)放在内部 for 之外。
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;
}
Inspect that now I move the Class.forName(fqnModel) outside the while loop. Because surely we only need to create Class object only once. But then, before each for loop, I create a model object that eventually will be added in a ArrayList.
检查现在我将 Class.forName(fqnModel) 移到 while 循环之外。因为当然我们只需要创建一次 Class 对象。但是,在每个 for 循环之前,我创建了一个最终将添加到 ArrayList 中的模型对象。
To be clear, this is my BiroModel class look like:
需要明确的是,这是我的 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;
}
}
}
I create a convention here, that all field object should be declared public. But, because I need EL syntax, I still need to create getter/setter for this public field.
我在这里创建了一个约定,即所有字段对象都应声明为 public。但是,因为我需要 EL 语法,我仍然需要为这个公共字段创建 getter/setter。

