Comment obtenir des types de parameters en utilisant la reflection?

Je veux utiliser des fonctions ayant différents nombres de parameters. Le problème est que je ne connais pas le nombre de parameters de chaque fonction et que je ne connais pas les noms des fonctions car ils sont stockés dans un tableau. Je ne connais que le nom de la classe, mais je ne souhaite pas utiliser getDeclaredMethods car cela augmenterait le temps de recherche. Est-il possible d’obtenir les types de parameters pour chaque fonction?

Ce que je fais habituellement lorsque je dois rechercher des méthodes, c’est générer une clé de cache à partir de la requête que je fais et enregistrer le résultat de la recherche avec cette clé de cache dans une carte.

Exemple:

Je connais les parameters de la méthode: Boolean.TRUE , Arrays.asList("foo","bar","baz") et BigInteger.valueOf(77777l)

Ma classe contient une méthode avec la signature

 public foo(boolean, Collection, Number) 

Il est impossible de mapper directement les parameters sur les types de parameters, car je ne sais tout simplement pas laquelle des super-classes ou interfaces est le type de paramètre, comme le montre le tableau suivant:

 Expected Type | What I have ----------------------------------------------------- boolean | java.lang.Boolean java.util.Collection | java.util.Arrays$ArrayList java.lang.Number | java.math.BigInteger 

Chacune de ces paires est compatible, mais il n’y a aucun moyen de trouver la méthode compatible sans définir une méthode de comparaison, quelque chose comme ceci:

 // determine whether a method's parameter types are compatible // with my arg array public static boolean isCompatible(final Method method, final Object[] params) throws Exception{ final Class[] parameterTypes = method.getParameterTypes(); if(params.length != parameterTypes.length){ return false; } for(int i = 0; i < params.length; i++){ final Object object = params[i]; final Class paramType = parameterTypes[i]; if(!isCompatible(object, paramType)){ return false; } } return true; } // determine whether a single object is compatible with // a single parameter type // careful: the object may be null private static boolean isCompatible(final Object object, final Class paramType) throws Exception{ if(object == null){ // primitive parameters are the only parameters // that can't handle a null object return !paramType.isPrimitive(); } // handles same type, super types and implemented interfaces if(paramType.isInstance(object)){ return true; } // special case: the arg may be the Object wrapper for the // primitive parameter type if(paramType.isPrimitive()){ return isWrapperTypeOf(object.getClass(), paramType); } return false; } /* awful hack, can be made much more elegant using Guava: return Primitives.unwrap(candidate).equals(primitiveType); */ private static boolean isWrapperTypeOf(final Class candidate, final Class primitiveType) throws Exception{ try{ return !candidate.isPrimitive() && candidate .getDeclaredField("TYPE") .get(null) .equals(primitiveType); } catch(final NoSuchFieldException e){ return false; } catch(final Exception e){ throw e; } } 

Donc, ce que je ferais, c’est un cache de méthode:

 private static final Map> methodCache; 

et ajoutez une méthode de recherche comme ceci:

 public static Set getMatchingMethods(final Class clazz, final Object[] args) throws Exception{ final Ssortingng cacheKey = toCacheKey(clazz, args); Set methods = methodCache.get(cacheKey); if(methods == null){ final Set tmpMethods = new HashSet(); for(final Method candidate : clazz.getDeclaredMethods()){ if(isCompatible(candidate, args)){ tmpMethods.add(candidate); } } methods = Collections.unmodifiableSet(tmpMethods); methodCache.put(cacheKey, methods); } return methods; } private static Ssortingng toCacheKey(final Class clazz, final Object[] args){ final SsortingngBuilder sb = new SsortingngBuilder(clazz.getName()); for(final Object obj : args){ sb.append('-').append( obj == null ? "null" : obj.getClass().getName()); } return sb.toSsortingng(); } 

Ainsi, les recherches ultérieures prendront beaucoup moins de temps que la première (pour les parameters du même type).

Bien sûr, étant donné que Class.getDeclaredMethods() utilise un cache en interne, la question est de savoir si mon cache améliore les performances. C’est essentiellement une question de ce qui est plus rapide:

  1. générer une clé de cache et interroger un HashMap ou
  2. itérer sur toutes les méthodes et demander la compatibilité des parameters

Mon hypothèse: pour les classes nombreuses (plusieurs méthodes), la première méthode va gagner