设置公共成员时出现IllegalArgumentException

时间:2009-05-28 09:24:44

标签: java reflection

我一直在玩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)

感谢。

6 个答案:

答案 0 :(得分:8)

Field.set方法的第一个参数应该是您要反思的对象。

decfields[0].set("msg", "success");

应阅读:

decfields[0].set(test, "success");

此外,最后的System.out.println调用应引用test对象而不是类ColinTest,因为我认为其目的是输出test.msg的内容字段。

<强>更新

正如toolkitChris所指出的,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。