2009-05-28 72 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。