How to get “double.class” by a “Double” Object

description:

  • I need use getMethod, it requires the parameterTypes.
  • The origin method requires double (a primitive type, not Double), and I can’t change origin method.
  • I can’t just input double.class in parameterTypes, because the s maybe diffierent types, such as Integer(not int).
  • The method parameter in Foo.java are always and only primitive types.

code:

test.java
    public static void main( String args[] )
    {
        Object obj = new Foo();
        Object s = 1.2;
        String type = "Double";
        try {
            Method method = obj.getClass().getMethod("return" + type, s.getClass());// got NoSuchMethodException here, because it requires `double` not Double
            System.out.println(method.invoke(obj,s));
        } catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
Foo.java //(I can't change/add code/delete in this part)
public class Foo {
    public double returnDouble(double type){
        return type;
    }
    public int returnInt(int type){
        return type;
    }
}

what I have tried:

  • Use Map
    public static void main( String args[] )
    {
        Object obj = new Foo();
//        Object s = 1;
//        String type = "Int";
        Object s = 1.2;
        String type = "Double";
        Map<String, Class> methodClassMap = new HashMap<String, Class>() {{
            put("Double",double.class);
            put("Integer",int.class);
        }};
        try {
            Method method = obj.getClass().getMethod("return" + type, methodClassMap.get(s.getClass().getSimpleName()));
            System.out.println(method.invoke(obj,s));
        } catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
  • It worked, but I have to list all possible type of value the s.

question:

  • Any better solution than using Map? Maybe use generic?

Answer

When you know beforehand that the target method always uses a primitive types, you can use the unwrap() method of MethodType of the java.lang.invoke package.

Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
    MethodType mt = MethodType.methodType(s.getClass(), s.getClass()).unwrap();
    Method method = obj.getClass().getMethod("return" + type, mt.parameterArray());
    System.out.println(method.invoke(obj, s));
} catch(ReflectiveOperationException e) {
    e.printStackTrace();
}

Alternatively, when you’re already using the method type of the java.lang.invoke package, you can also use a method handle to perform the invocation.

Object obj = new Foo();
Object s = 1.2;
String type = "Double";
try {
    MethodType mt = MethodType.methodType(s.getClass(), s.getClass()).unwrap();
    MethodHandle mh = MethodHandles.lookup().bind(obj, "return" + type, mt);
    System.out.println(mh.invoke(s));
} catch(Throwable e) {
    e.printStackTrace();
}

But note that unlike Reflection, the return type has to be correctly specified for the lookup. I’m assuming the same return type as the parameter type, like in your example.