2011-02-11 88 views
8

我想使用具有不同參數數量的函數。問題是我不知道每個函數的參數數量,也不知道函數的名稱,因爲它們存儲在一個數組中。我只知道班級名稱,但不想使用getDeclaredMethods,因爲它會增加搜索時間。有沒有辦法爲每個函數獲取參數類型?如何使用反射獲取參數類型?

+1

*,因爲它們存儲在一個數組*誰是'他們'?方法或參數?請展示一些代碼來說明 – 2011-02-11 09:52:05

回答

4

當我必須查找方法時,我通常會做的是從查詢中生成一個緩存鍵,並將該緩存鍵的搜索結果保存在地圖中。

例子:

我知道方法參數Boolean.TRUEArrays.asList("foo","bar","baz")BigInteger.valueOf(77777l)

我的類包含與簽名的方法

public foo(boolean, Collection, Number) 

有沒有辦法,我可以將參數直接映射參數類型,因爲我只是不知道哪個超類或接口是參數類型,如下表所示:

這些對每一個是兼容的,但是沒有辦法找到兼容的方法沒有定義的比較方法,像這樣:

// 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; 
    } 
} 

所以我會做的是有一個方法緩存:

private static final Map<String, Set<Method>> methodCache; 

和添加的查找方法是這樣的:

public static Set<Method> getMatchingMethods(final Class<?> clazz, 
    final Object[] args) throws Exception{ 
    final String cacheKey = toCacheKey(clazz, args); 
    Set<Method> methods = methodCache.get(cacheKey); 
    if(methods == null){ 
     final Set<Method> tmpMethods = new HashSet<Method>(); 
     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 String toCacheKey(final Class<?> clazz, final Object[] args){ 
    final StringBuilder sb = new StringBuilder(clazz.getName()); 
    for(final Object obj : args){ 
     sb.append('-').append(
      obj == null ? "null" : obj.getClass().getName()); 
    } 
    return sb.toString(); 
} 

這樣,隨後的查找將比第一個查找花費更少的時間(對於相同類型的參數)。

當然,因爲Class.getDeclaredMethods()在內部使用緩存,所以問題是我的緩存是否改善了性能。它基本上是一個什麼樣的速度更快問題:

  1. 生成緩存鍵和查詢一個HashMap或
  2. 遍歷所有的方法和查詢參數兼容性

我的猜測:大班(許多方法),第一種方法將獲勝,否則第二種方法將