简体   繁体   中英

Want to pass class.getmethod arguments dynamically in run time java

I want to invoke a method from by giving methodName in runtime. I can do it in below way.

Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

But I want to list down all the overloaded methods with that method name and the different set of arguments and let the user choose a particular overloaded method and dynamically pass those arguments in

Method method = MyObject.class.getMethod("doSomething", String.class);

instead of hardcoding String.class .

suppose I have two methods like

methodName(String) 

and overloaded method

methodName(String, int)

I want to let the user choose which one to pick in runtime and pass that information for getMethod function for that particular method.

How can I do this?

We have a method called Class.forName(String) to load an instance of Class<?> by its name.

The problem is that we have to pass the fully qualified name of the desired class (including the name of a package). It means that Class.forName("String") is not going to work. Instead, we need to call it as Class.forName("java.lang.String") .

We could have a map (or enum) to keep those Class<?> es. Since we are expecting the user to collaborate, keys would be String s and the structure would be like Map<String, Class<?>> :

user > string
we   < class java.util.String

Then, we should figure out how to parse method arguments according to their types - they are going to come as String s. I would utilise a Function<String, ?> per type:

Function<String, T> converter = (String s) -> T.convertFromString(s);

To make it more clear for you, I wrote a simple, complete example for you:

class Test {
    // prints s once
    public static void method(String s) {
        System.out.println(s);
    }

    // prints s i times
    public static void method(String s, int i) {
        System.out.println(IntStream.rangeClosed(0, i - 1)
                .mapToObj($ -> s)
                .collect(Collectors.joining(" ")));
    }

    public static void main(String[] args) {
        perform();
    }

    public static Object perform() {
        final Scanner scanner = new Scanner(System.in);

        // read the method name
        final String methodName = scanner.nextLine();

        final Method[] methods = Arrays.stream(Test.class.getDeclaredMethods())
                .filter(m -> m.getName().endsWith(methodName) && !m.isSynthetic())
                .toArray(Method[]::new);

        // read the method parameter types in the format "type1 type2"
        final String rawMethodParametersTypes = scanner.nextLine();

        final SupportedType[] methodParameterTypes = Arrays.stream(rawMethodParametersTypes.split(" "))
                .map(p -> SupportedType.valueOf(p.toUpperCase()))
                .toArray(SupportedType[]::new);

        final Optional<Method> selectedMethod = Arrays.stream(methods)
                .filter(m -> Arrays.equals(Arrays.stream(methodParameterTypes)
                        .map(SupportedType::getType).toArray(Class<?>[]::new), m.getParameterTypes()))
                .findAny();

        if (!selectedMethod.isPresent()) {
            return null;
        }

        final Method method = selectedMethod.get();

        // read method arguments in the format "arg1 arg2"
        final String rawMethodArgumentsLine = scanner.nextLine();
        final String[] rawMethodArguments = rawMethodArgumentsLine.split(" ");

        final int expectedLength = method.getParameterCount();
        if (rawMethodArguments.length != expectedLength) {
            return null;
        }

        Object[] methodArguments = new Object[expectedLength];
        for (int i = 0; i < expectedLength; ++i) {
            methodArguments[i] = methodParameterTypes[i].getConverter().apply(rawMethodArguments[i]);
        }

        try {
            return method.invoke(null, methodArguments);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }
}

I introduced an enum SupportedType to declare the types we are going to support and which the user might encounter with while choosing the signature.

@RequiredArgsConstructor
public enum SupportedType {
    STRING(String.class, s -> s),
    INT(int.class, Integer::valueOf);

    @Getter
    private final Class<?> type;

    @Getter
    private final Function<String, Object> converter;
}

Here are input-output examples for method(String, int)

> method
> string int
> hello 5
< hello hello hello hello hello

and method(String)

> method
> string
> hello
< hello

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