2009-05-28 75 views
2

我一直在玩反思Java ...我有点困惑。IllegalArgumentException当设置公共成员

我希望下面的程序能让我改变一个类中公共成员变量的值。但是,我收到一个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) 

感谢。

回答

7

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方法的msgField可以调用为:

msgField.set(test, "success"); 

这种方式有其优点,正如工具箱所指出的那样,如果有更多的字段添加到对象中,Class.getDeclaredFields返回的字段的顺序可能不一定返回字段msg作为数组的第一个元素。根据返回数组的顺序以某种方式,当对类进行更改时可能会导致问题。

因此,使用getDeclaredField并声明所需字段的名称可能会更好。

+0

如果您打算在将来添加更多字段,请注意使用`decFields [0]`! – toolkit 2009-05-28 09:50:37

2

要设置的第一个参数()应该是您要更改的字段的对象...即测试。

1

当你调用getDeclaredFields时,每个数组元素将包含一个Field对象,该对象代表该类上的一个字段,而在一个实例化对象上为否。

这就是为什么你必须指定要在其上设置该字段的对象,使用设置方法时:

ColinTest test = new ColinTest(); 
Field msgfield = ColinTest.class.getDeclaredField("msg"); 
msgField.set(test, "success"); 
+0

你的第一个参数应该是测试,而不是c。 – toolkit 2009-05-28 09:32:44

1

你想要的是:

Field msgField = c.getDeclaredField("msg"); 
msgField.set(test, "Success"); 

以使用decfields[0]小心,因为当您为班级添加第二个字段时,您可能得不到预期的结果(您未检查decfields[0]对应于msg字段)

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) 

这可能会导致你解决其他人发布。

0

我偶然发现了这个页面,因为奇怪的是,我不能在我的类中设置公共字符串字段。代码将在每个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; 
} 

检查我现在将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。