Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unboxing and Reflection in Java?

I have a method with the signature public void setFoo(int newFoo) in a model I'm writing. I'm using the following code to call it from within my controller:

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

Calling this method like controller.setModelProperty("Foo",5); results in an exception being thrown: java.lang.NoSuchMethodException: foo.bar.models.FooModel.setFoo(java.lang.Integer) -- it looks like the int is being boxed as an Integer, which doesn't match the signature of setFoo.

Is there any way to convince this reflection code to pass 5 (or whatever int I pass in) as an Integer, without the boxing? Or do I have to create public void setFoo(Integer newFoo) in my model and unbox explicitly, then call the original setFoo?

like image 940
Alterscape Avatar asked Dec 05 '25 00:12

Alterscape


2 Answers

You could specialise your setModelProperty for any primitives you expect to be used with:

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

Alternatively, you could use instanceof on newValue to check for boxed primitives:

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

Or finally, if you have the source for setFoo, you could change it to take a boxed Integer instead of an int - the overhead is usually negligible.

like image 111
MHarris Avatar answered Dec 07 '25 15:12

MHarris


Is there any way to convince this reflection code to pass 5 (or whatever int I pass in) as an Integer, without the boxing?

Not while your method signature says Object newValue, because an int can never be an Object. A way to keep the method generic would be to have callers pass in the type explicitly, i.e.:

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

Alternatively, you could test the type of newValue to see if it's a primitive wrapper and in that case look for both the primitive and the wrapped version of the method. However, that won't work when the user passes in a null. Actually, the method won't work at all in that case...

like image 21
Michael Borgwardt Avatar answered Dec 07 '25 14:12

Michael Borgwardt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!