2010-06-21 17 views
8

Tenir compte l'extrait suivant:En utilisant des méthodes java.lang.reflect.getMethod polymorphes

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() est évidemment pas au courant qu'un String pourrait être amenée à une méthode qui attend un Object (en effet, il est dit que la documentation il recherche méthode avec le nom spécifié et exactement les mêmes types de paramètres formels).

est-il un moyen simple de trouver des méthodes pensivement, comme getMethod() fait, mais en prenant en compte le polymorphisme, de sorte que l'exemple de réflexion ci-dessus pourrait trouver la méthode print(Object) lors d'une interrogation avec ("print", String.class) paramètres?

+1

Je dirige un problème similaire dans http://stackoverflow.com/questions/2169497/unexpected-class-getmethod-behavior et il n'y avait pas moyen de le faire en itérant sur tous les arguments et en appelant isAssignableFrom – Daff

Répondre

8

Le reflection tutorial

suggèrent l'utilisation de Class.isAssignableFrom() échantillon pour trouver 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; 
     } 
    } 
1

La façon facile de le faire est via java.beans.Statement ou java.beans.Expression. Est-ce que tous ces chantiers difficiles pour vous.

getMethod() est évidemment pas au courant que une chaîne peut être alimenté à une méthode qui attend un objet

« Inconscient » est une étrange façon de le mettre. getMethod() adhère à ses spécifications. Vous devez fournir les paramètres formels, pas les types des arguments réels.

1

FYI, c'est comment j'appelle la méthode en utilisant la réflexion avec plusieurs paramètres sans donner leurs 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; 
    } 

} 

Exemple d'utilisation:

MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }, new Class[] { String.class, Collection.class }); 
    MyMethodUtils.invoke(student, "setNameAndMarks", new Object[] { "John", marks }); 

Cependant, pour la méthode invoke(Object invoker, String methodName, Object[] parameters), il est possible d'invoquer mauvaise méthode si la signature est ambiguë. Par exemple, s'il y a deux méthodes pour l'invocateur:

public void setNameAndMarks(String name, Collection<Integer> marks); 
public void setNameAndMarks(String name, ArrayList<Integer> marks); 

Passing le paramètre suivant peut invoquer mauvaise méthode

setNameAndMarks("John", new ArrayList<Integer>());