2009-05-28 13 views
2

Javaでリフレクションをしています...私はちょっと困惑しています。publicメンバーを設定するとIllegalArgumentExceptionが発生する

私は、以下のプログラムでクラス内のパブリックメンバー変数の値を変更できることを期待していました。しかし、私はIllegalArgumentExceptionを受け取ります。何か案は?

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) 

おかげ -

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) 
    } 
} 

私はこのメッセージが表示されます。

答えて

7

Field.setメソッドの最初の引数は、反映しているオブジェクトである必要があります。

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

を読んでください、私は意図は出力に test.msgフィールドの内容であると推測として

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

さらに、最終System.out.printlnコールは、testオブジェクトではなくクラスColinTestを参照してください。その後

Field msgField = test.getClass().getDeclaredField("msg"); 

// or alternatively: 

Field msgField = ColinTest.class.getDeclaredField("msg"); 

set方法:

更新toolkitChrisで指摘したように

Class.getDeclaredField方法は、それを取得するために、フィールドの名前を指定するために使用することができますmsgFieldの次のように呼び出すことができます。

msgField.set(test, "success"); 

この方法は、すでにツールキットによって指摘されているように、オブジェクトに追加されたフィールドがさらにある場合、Class.getDeclaredFieldsによって返されるフィールドの順序は、配列の最初の要素としてフィールドmsgを返すとは限りません。返される配列の順序によっては、クラスに変更が加えられたときに問題が発生することがあります。

したがって、おそらくgetDeclaredFieldを使用して、希望するフィールドの名前を宣言することをお勧めします。

+0

今後さらにフィールドを追加する予定がある場合は、 'decFields [0]'を使用してください。 – toolkit

2

最初のarg()は、フィールドを変更しているオブジェクト、つまりtestでなければなりません。

1

getDeclaredFieldsを呼び出すと、各配列要素には、クラス上のフィールドを表すFieldオブジェクトが含まれます(インスタンス化されたオブジェクトではno)。あなたがセッターを使用しているとき、あなたはそのフィールドを設定するオブジェクトを指定する必要が理由です

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

最初の引数はcでなくtestでなければなりません。 – toolkit

+0

ありがとう、投稿した直後もそれを見たので、すぐに編集で修正しました;-) –

1

をあなたが望むものは次のとおりです。

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

decfields[0]以来を使用して注意してくださいクラスに2番目のフィールドを追加したときに期待したものが得られない可能性があります(decfields[0]msgフィールドに対応していることを確認していません)

2

投稿したコードが実際にコンパイルされていることを確認してください(ではなく、test.msgが必要です)。

また、より具体的なエラーメッセージを提供する可能性がある、ジャワの新しいバージョンを使用して検討してください。

% 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に新しい行を追加します。問題は、私は新しいオブジェクトのインスタンス化コードを挿入することです。

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; 
} 

}

私はすべてのフィールドオブジェクトがパブリック宣言されなければならないことを、ここで規則を作成します。しかし、私はELの構文が必要なので、私はまだこのパブリックフィールドのgetter/setterを作成する必要があります。

関連する問題