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:
Mon hypothèse: pour les classes nombreuses (plusieurs méthodes), la première méthode va gagner