Распаковка и отражение в Java? - PullRequest
2 голосов
/ 08 июля 2010

У меня есть метод с подписью public void setFoo(int newFoo) в модели, которую я пишу.Я использую следующий код для вызова изнутри моего контроллера:

protected void setModelProperty(String propertyName, Object newValue) {

    for (AbstractModel model: registeredModels) {
        try {
            Method method = model.getClass().
                getMethod("set"+propertyName, new Class[] {
                                                  newValue.getClass()
                                              }
                         );
            method.invoke(model, newValue);

        } catch (Exception ex) {
            //  Handle exception.
            System.err.println(ex.toString());
        }
    }
}

Вызов этого метода наподобие controller.setModelProperty("Foo",5); приводит к исключению: java.lang.NoSuchMethodException: foo.bar.models.FooModel.setFoo(java.lang.Integer) - похоже на intупаковывается как Integer, что не соответствует сигнатуре setFoo.

Есть ли способ убедить этот код отражения передать 5 (или что-то еще, что я передаю) как целое число, без бокса?Или мне нужно создать public void setFoo(Integer newFoo) в моей модели и явно распаковать, а затем вызвать оригинальный setFoo?

Ответы [ 6 ]

4 голосов
/ 08 июля 2010

Вы можете специализировать setModelProperty для любых примитивов, которые вы ожидаете использовать с:

protected void setModelProperty(String propertyName, int newValue) { /* ... */ }

В качестве альтернативы, вы можете использовать instanceof на newValue для проверки примитивов в штучной упаковке:

Class[] classes;
if (newValue instanceof Integer) {
  classes = new Class[] { int.class };
} else if (newValue instanceof Double) {
  /* etc...*/
} else {
  classes = new Class[] {newValue.getClass() };
}

Или, наконец, если у вас есть источник для setFoo, вы можете изменить его на целочисленное в штучной упаковке вместо целого числа - накладные расходы обычно незначительны.

2 голосов
/ 08 июля 2010

Есть ли способ убедить это код отражения для прохождения 5 (или все, что я передаю) как целое число, без бокса?

Нет, пока подпись вашего метода говорит Object newValue, потому что int никогда не может быть Object. Способ сохранить универсальный метод состоял бы в том, чтобы вызывающие стороны передавали тип явно, т.е.

protected void setModelProperty(String propertyName, Object newValue, Class type) {

В качестве альтернативы, вы можете проверить тип newValue, чтобы увидеть, является ли это примитивной оболочкой, и в этом случае искать как примитивную, так и упакованную версию метода. Однако это не сработает, когда пользователь передаст null. На самом деле, метод не будет работать вообще в этом случае ...

1 голос
/ 08 июля 2010

newValue упаковывается как Integer, когда вызывается setModelProperty (). Это единственный способ, которым это можно назвать; int не является экземпляром Object. newValue.getClass () возвращает "Integer", и, следовательно, вызов getMethod () завершается неудачей.

Если вы хотите работать с примитивами, вам понадобится специальная версия setModelProperty. В качестве альтернативы вы можете написать метод setFoo (Integer).

Или, в общем, вы могли бы написать:

if (newValue.getClass()==Integer.class) {
  // code to look for a method with an int argument
}
0 голосов
/ 08 июля 2010

Outoboxing будет сделано, но вы ищете неправильный метод.Массив типа параметра неверен.Для int тип будет Integer.TYPE вместо Integer.class.

Попробуйте сделать что-то вроде следующего, чтобы заставить его работать независимо от типа параметра (это будет делать аутобокс):

protected void setModelProperty(String propertyName, Object newValue) {

    for (AbstractModel model: registeredModels) {
        try {
            Method method = findSetter(model.getClass(), propertyName);
            method.invoke(model, newValue);

        } catch (Exception ex) {
            //  Handle exception.
            System.err.println(ex.toString());
        }
    }
}

private Method findSetter(Class<?> type, String propertyName) {
    for (Method methos : type.getMethods()) {
        if (method.getName().equalsIgnoreCase("set" + propertyName) && method.getParameterTypes().length == 1) {
            return method;
        }
    }
    throw new NoSuchMethodError("No setter for " + propertyName);
}
0 голосов
/ 08 июля 2010

Проблема не в вызове, а в получении метода, так как вы используете Integer.class, который отличается от int.class.

Это можно исправить, выполнив второй вызов метода get, если первыйодна ошибка:

protected void setModelProperty(String propertyName, Object newValue) {

    for (AbstractModel model: registeredModels) {
        Method method = null;
        try {
            method = model.getClass().
                 getMethod("set"+propertyName, new Class[] {
                                                  newValue.getClass()
                                               }
                         );
        } catch (Exception ex) {
            try {
                if (canBoxPrimitive(newValue.getClass()) {
                    method = model.getClass().
                           getMethod("set"+propertyName, new Class[] {
                                                  primitiveClass(newValue.getClass())
                                               }
                }
            }
            catch (Exception ex) {
                // handle it
            }
        }

        method.invoke(model, newValue);
   }
}

Для опций кодирования функций: логический canBoxPrimitive (Class) и Class primitiveClass (Class) вы можете посмотреть здесь: Динамически найти класс, представляющий примитивный тип Java

0 голосов
/ 08 июля 2010

Вы пробовали

Integer.valueOf(5) чтобы передать 5 как целое число вместо целого?

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