Consider the following snippet:
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test = new ReflectionTest();
String object = new String("Hello!");
// 1. String is accepted as an Object
test.print(object);
// 2. The appropriate method is not found with String.class
try {
java.lang.reflect.Method print
= test.getClass().getMethod("print", object.getClass());
print.invoke(test, object);
} catch (Exception ex) {
ex.printStackTrace(); // NoSuchMethodException!
}
}
public void print(Object object) {
System.out.println(object.toString());
}
}
getMethod()
is obviously unaware that a String
could be fed to a method that expects an Object
(indeed, it's documentation says that it looks for method with the specified name and exactly开发者_JS百科 the same formal parameter types).
Is there a straightforward way to find methods reflectively, like getMethod()
does, but taking polymorphism into account, so that the above reflection example could find the print(Object)
method when queried with ("print", String.class)
parameters?
The reflection tutorial
suggest the use of Class.isAssignableFrom()
sample for finding print(String)
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.startsWith("print") {
continue;
}
Type[] pType = m.getGenericParameterTypes();
if ((pType.length != 1)
|| !String.class.isAssignableFrom(pType[0].getClass())) {
continue;
}
}
The easy way to do this is via java.beans.Statement or java.beans.Expression. Does all these hard yards for you.
getMethod() is obviously unaware that a String could be fed to a method that expects an Object
'Unaware' is a strange way to put it. getMethod()
adheres to its specification. You have to supply the formal parameters, not the types of the actual arguments.
FYI, it is how I invoke method using reflection with multiple parameters without giving their types.
public class MyMethodUtils {
/**
* Need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters, Class[] parameterClasses) throws Exception {
Method method = invoker.getClass().getMethod(methodName, parameterClasses);
Object returnValue = method.invoke(invoker, parameters);
return returnValue;
}
/**
* No need to pass parameter classes
*/
public static Object invoke(Object invoker, String methodName, Object[] parameters) throws Exception {
Method[] allMethods = invoker.getClass().getDeclaredMethods();
Object returnValue = null;
boolean isFound = false;
for (Method m : allMethods) {
String mname = m.getName();
if (!mname.equals(methodName)) {
continue;
}
Class[] methodParaClasses = m.getParameterTypes();
for (int i = 0; i < methodParaClasses.length; i++) {
Class<?> parameterClass = parameters[i].getClass();
Class<?> methodParaClass = methodParaClasses[i];
boolean isAssignable = methodParaClass.isAssignableFrom(parameterClass);
if (!isAssignable) {
continue;
}
}
returnValue = m.invoke(invoker, parameters);
isFound = true;
}
if (!isFound) {
throw new RuntimeException("Cannot find such method");
}
return returnValue;
}
}
Sample Usage:
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }, new Class[] { String.class, Collection.class });
MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks });
However, for the method invoke(Object invoker, String methodName, Object[] parameters)
, it is possible to invoke wrong method if the signature is ambiguous. For example, if there is two methods for the invoker:
public void setNameAndMarks(String name, Collection<Integer> marks);
public void setNameAndMarks(String name, ArrayList<Integer> marks);
Passing the following parameter may invoke wrong method
setNameAndMarks("John", new ArrayList<Integer>());
精彩评论