Java 如何使用反射获取参数类型?

Java 如何使用反射获取参数类型?,java,reflection,Java,Reflection,我想使用具有不同数量参数的函数。问题是,我不知道每个函数的参数数量,也不知道存储在数组中的函数名。我只知道类名,但不想使用getDeclaredMethods,因为这会增加搜索时间。有没有办法获取每个函数的参数类型?当我必须查找方法时,我通常做的是从我正在执行的查询中生成一个缓存键,并使用该缓存键将搜索结果保存在映射中 例如: 我知道方法参数是Boolean.TRUE,Arrays.asList(“foo”,“bar”,“baz”)和biginger.valueOf(77777 l) 我的类包含

我想使用具有不同数量参数的函数。问题是,我不知道每个函数的参数数量,也不知道存储在数组中的函数名。我只知道类名,但不想使用
getDeclaredMethods
,因为这会增加搜索时间。有没有办法获取每个函数的参数类型?

当我必须查找方法时,我通常做的是从我正在执行的查询中生成一个缓存键,并使用该缓存键将搜索结果保存在映射中

例如:

我知道方法参数是
Boolean.TRUE
Arrays.asList(“foo”,“bar”,“baz”)
biginger.valueOf(77777 l)

我的类包含一个带有签名的方法

public foo(boolean, Collection, Number)
我无法直接将参数映射到参数类型,因为我不知道哪个超类或接口是参数类型,如下表所示:

Expected Type          |  What I have
-----------------------------------------------------
 boolean               |  java.lang.Boolean
 java.util.Collection  |  java.util.Arrays$ArrayList
 java.lang.Number      |  java.math.BigInteger
这些对中的每一对都是兼容的,但是如果不定义比较方法,就无法找到兼容的方法,例如:

// 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;
    }
}
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();
}
//确定方法的参数类型是否兼容
//使用我的arg数组
公共静态布尔值可兼容(最终方法,
最终对象[]参数)引发异常{
最终类[]parameterTypes=method.getParameterTypes();
if(params.length!=parameterTypes.length){
返回false;
}
对于(int i=0;i
所以我要做的是有一个方法缓存:

private static final Map<String, Set<Method>> methodCache;
私有静态最终映射方法缓存;
并添加如下查找方法:

// 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;
    }
}
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();
}
公共静态集合getMatchingMethods(最终类clazz,
最终对象[]args)引发异常{
最终字符串cacheKey=toCacheKey(clazz,args);
Set methods=methodCache.get(cacheKey);
if(方法==null){
最终设置tmpMethods=newhashset();
for(最终候选方法:clazz.getDeclaredMethods()){
if(不可竞争(候选人,参数)){
tmpMethods.add(候选者);
}
}
方法=Collections.unmodifiableSet(tmpMethods);
put(cacheKey,methods);
}
返回方法;
}
私有静态字符串toCacheKey(最终类clazz,最终对象[]args){
最终StringBuilder sb=新StringBuilder(clazz.getName());
用于(最终对象对象:args){
某人附加('-')。附加(
obj==null?“null”:obj.getClass().getName());
}
使某人返回字符串();
}
这样,后续查找将比第一次(对于相同类型的参数)花费更少的时间

当然,由于
Class.getDeclaredMethods()
在内部使用缓存,问题是我的缓存是否能提高性能。这基本上是一个什么更快的问题:

  • 生成缓存键并查询哈希映射或
  • 迭代所有方法并查询参数兼容性

  • 我猜:对于大型类(许多方法),第一个方法会赢,否则第二个方法会赢,因为它们存储在一个数组中,谁是“它们”?方法还是参数?请给出一些代码来说明我不太明白使用“getDeclaredMethods”如何“增加搜索时间”。。。您想要的是所有方法的参数类型,对吗?然后你必须迭代所有的方法IIUC…getDeclaredMethods缓存它得到的结果,这样重复调用一个类不会对性能造成太大影响。我认为这个问题很模糊。请澄清问题,最好添加说明问题的相关代码。如果要“获取每个函数的参数类型”,需要使用getDeclaredMethods。这就是“每个功能”的本质!