2009-05-28 2 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 поля.

Update

Как отметил toolkit и Chris, метод 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

2

Первым аргументом для установки() должен быть объект, поле которого вы меняете ... а именно тест.

1

Когда вы вызываете getDeclaredFields, каждый элемент массива будет содержать объект Field, который представляет поле в классе, а не на экземпляр объекта.

Вот почему вы должны указать объект, на котором вы хотите установить это поле, при использовании сеттер:

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

Ваш первый аргумент должен быть тестом, а не c. – toolkit

+0

Спасибо, я тоже видел это сразу после публикации, поэтому я сразу исправил его в редактировании ;-) –

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

Я наткнулся на эту страницу, потому что странно, я не могу установить публичное поле в моем классе. Код добавит новую строку в 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) вне цикла. Потому что, конечно, нам нужно только создать объект класса только один раз. Но затем, перед каждым циклом 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 для этого публичного поля.

Смежные вопросы