Java中的拆箱和反射?

Java中的拆箱和反射?,java,reflection,Java,Reflection,在我正在编写的模型中,我有一个签名为public void setFoo(int newFoo)的方法。我正在使用以下代码从控制器中调用它: protected void setModelProperty(String propertyName, Object newValue) { for (AbstractModel model: registeredModels) { try { Method method = model.getClass

在我正在编写的模型中,我有一个签名为
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
被装箱为
整数
,与setFoo的签名不匹配

有没有办法说服这个反射代码以整数形式传递
5
(或者我传递的任何整数),而不使用装箱?或者我必须在我的模型中创建
public void setFoo(Integer newFoo)
并显式取消装箱,然后调用原始setFoo吗

你试过了吗


Integer.valueOf(5)
要将5作为整数而不是整数传递?

您可以将您的
setModelProperty专门用于您希望使用的任何原语:

protected void setModelProperty(String propertyName, int newValue) { /* ... */ }
或者,您可以在newValue上使用instanceof检查装箱原语:

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

最后,如果您有setFoo的源代码,您可以将其更改为一个装箱整数而不是整数-开销通常可以忽略。

调用setModelProperty()时,newValue被装箱为整数。这是唯一可以称之为的方法;'int'不是对象的实例。getClass()返回“整数”,因此对getMethod()的调用失败

如果您想在这里使用原语,您需要一个特殊版本的setModelProperty。或者,您可以编写一个方法setFoo(Integer)

或者更一般地说,你可以写:

if (newValue.getClass()==Integer.class) {
  // code to look for a method with an int argument
}
有什么办法可以说服这一点吗 反射代码通过5(或 无论我传入什么int)作为整数, 没有拳击

当您的方法签名显示
对象newValue
时不会,因为int永远不能是
对象
。保持方法泛型的一种方法是让调用方显式传入类型,即:

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

或者,您可以测试
newValue
的类型,看看它是否是一个原语包装器,在这种情况下,可以同时查找方法的原语和包装版本。但是,当用户传入一个
null
时,这将不起作用。实际上,在这种情况下,该方法根本不起作用…

问题不在于调用,而在于获取该方法,因为您使用的是Integer.class,它与int.class不同

如果第一个方法失败,您可以通过执行第二个调用来修复它:

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

有关编写函数的选项:boolean CanboxPrimitiveClass(类)和Class primitiveClass(类),您可以在此处查看:

将完成OBObxing,但您正在寻找错误的方法。参数类型数组不正确。对于int,类型将是Integer.type而不是Integer.class

无论参数类型如何,都可以尝试以下方法使其正常工作(outoboxing可以做到这一点):

受保护的void setModelProperty(字符串propertyName,对象newValue){
for(抽象模型:registeredModels){
试一试{
方法Method=findSetter(model.getClass(),propertyName);
调用(model,newValue);
}捕获(例外情况除外){
//处理异常。
System.err.println(例如toString());
}
}
}
私有方法findSetter(类类型,字符串propertyName){
对于(方法methods:type.getMethods()){
if(method.getName().equalsIgnoreCase(“set”+propertyName)&&method.getParameterTypes().length==1){
返回法;
}
}
抛出新的NoSuchMethodError(“没有“+propertyName”的设置程序);
}

这很奇怪。Method.invoke()的Javadocs说,单个参数会自动展开以匹配原始形式参数,所以这似乎不应该是一个问题。我在这台机器上没有编译器,或者我想做一些测试。@mmyers,但是newValue是一个装箱整数,所以getMethod将在调用方法之前,尝试返回带有签名的(不存在的)方法
setFoo(Integer)
。invoke(…)。@MHarris:可以这样做。我看到了getMethod调用,但由于格式不寻常,我认为它是一个自我实现的方法,而不是标准的类方法。这样,如果它是一个普通对象,您可以重载该方法以接收2个参数,并且调用
setModelProperty(propertyName,newValue,newValue.getClass())
。但是,如果您知道值必须是基元类型,则可以使用params
String,int
创建一个方法,该方法调用
setModelProperty(propertyName,objectnewvalue,Integer.type)
。不过,对于要使用的每个基本体,都需要一个。
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);
}