简体   繁体   中英

How to dry run Method.invoke()

java.lang.reflect.Method.invoke() documentation states that it throws IllegalArgumentException

IllegalArgumentException - if the method is an instance method and the specified object argument is not an instance of the class or interface declaring the underlying method (or of a subclass or implementor thereof); if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion.

Is there a way to test if method throws IllegalArgumentException for given arguments without invoking it?

It is possible to check for correctness of instance, method and number of arguments manually and check for assignability of arguments by piggybacking on native java mechanism.

public class Methods {
  public static boolean isInvocable(Method method, Object instance,
      Object... arguments) {
    return correctInstance(instance, method)
        && correctArguments(arguments, method);
  }

  private static boolean correctInstance(Object instance, Method method) {
    return Modifier.isStatic(method.getModifiers())
        || method.getDeclaringClass().isInstance(instance);
  }

  private static boolean correctArguments(Object[] arguments, Method method) {
    Class<?>[] parameters = method.getParameterTypes();
    if (parameters.length != arguments.length) {
      return false;
    }
    for (int i = 0; i < parameters.length; i++) {
      if (!correctArgument(arguments[i], parameters[i])) {
        return false;
      }
    }
    return true;
  }

  private static boolean correctArgument(Object instance, Class<?> type) {
    return type.isPrimitive()
        ? correctPrimitive(instance, type)
        : instance == null || type.isAssignableFrom(instance.getClass());
  }

  private static boolean correctPrimitive(Object argument, Class<?> type) {
    try {
      Method method = Overloading.class.getDeclaredMethod("method", type);
      method.setAccessible(true);
      method.invoke(null, argument);
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    } catch (NoSuchMethodException e) {
      throw new Error(e);
    } catch (IllegalAccessException e) {
      throw new Error(e);
    } catch (InvocationTargetException e) {
      throw new Error(e);
    }
  }

  @SuppressWarnings("unused")
  private static class Overloading {
    private static void method(byte argument) {}

    private static void method(short argument) {}

    private static void method(int argument) {}

    private static void method(long argument) {}

    private static void method(float argument) {}

    private static void method(double argument) {}

    private static void method(boolean argument) {}

    private static void method(char argument) {}
  }
}

Yes, you can use getDeclaredMethod from the Reflection API to verify that a certain method with given signature is declared. An example can be found in the docs (on the bottom): http://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html

This might work as is, though I didn't test it:

public boolean hasMethod( Class<?> clazz, String methodName, Class[] argTypes ) {
    try {
        clazz.getDeclaredMethod( methodName, argTypes );
        return true;
    } catch( NoSuchMethodException e ) {
        return false;
    } catch( Exception e ) {
        // something else went wrong
        // you definitely wanna handle this case more gracefully
        throw new IllegalStateException( "Uh-Oh!", e );
    }
}

// example call for a method named someMethod that takes an array of strings
// as its only argument
Class[] argTypes = new Class[] { String[].class };
boolean hasMethod = hasMethod( yourClass, "someMethod", argTypes );

What you are asking

At runtime, first, method, instance and list of arguments is created and only some time later in different place in code it is invoked. I want to detect problems early on instead of wasting time and reporting exception with non-informative stack trace.

is not possible. For some of

IllegalArgumentException - if the method is an instance method and the specified object argument is not an instance of the class or interface declaring the underlying method (or of a subclass or implementor thereof); if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion.

you can check yourself. For example, you can do an instanceof on the instance or rather use Class#isInstance() on it. You can compare the number of arguments you have and the number of arguments the method requires. For type conversion of primitives, it's a little harder to test and might depend on actually knowing which types get converted.

Your best bet is to invoke it when you need to and handle the IllegalArgumentException .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM