IllegalArgumentException при настройке открытого члена - PullRequest
2 голосов
/ 28 мая 2009

Я играл с отражением в 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 ]

8 голосов
/ 28 мая 2009

Первый аргумент метода Field.set должен быть объектом, о котором вы размышляете.

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

Следует читать:

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

Кроме того, последний вызов System.out.println должен относиться к объекту test, а не к классу ColinTest, так как я предполагаю, что намерение - вывести содержимое поля test.msg.

Обновление

Как указано 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 и объявить имя нужного поля.

2 голосов
/ 28 мая 2009

Пожалуйста, убедитесь, что код, который вы публикуете, действительно компилируется (вы хотите 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)

, что, вероятно, привело бы вас к решению, опубликованному другими.

2 голосов
/ 28 мая 2009

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

1 голос
/ 28 мая 2009

То, что вы хотите:

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

Будьте осторожны, используя decfields[0], поскольку вы можете получить не то, что ожидали, добавив второе поле в свой класс (вы не проверяете, что decfields[0] соответствует полю msg)

1 голос
/ 28 мая 2009

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

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

ColinTest test = new ColinTest();
Field msgfield = ColinTest.class.getDeclaredField("msg");
msgField.set(test, "success");
0 голосов
/ 14 декабря 2010

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

}

Я создаю здесь соглашение о том, что весь объект поля должен быть объявлен открытым. Но, поскольку мне нужен синтаксис EL, мне все еще нужно создать getter / setter для этого открытого поля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...