Reputation: 89
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?
Upvotes: 3
Views: 4717
Reputation: 49606
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
Upvotes: 5