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

发布于 2024-10-17 11:10:05 字数 121 浏览 3 评论 0原文

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

I want to use functions having different numbers of parameters. The problem is that I don't know the number of parameters of each function, and also I don't know names of function as they are stored in an array. I only knows the class name, but don't want to use getDeclaredMethods as it will increase search time. Is there a way to get the parameter types for each function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦幻的心爱 2024-10-24 11:10:05

当我必须查找方法时,我通常所做的就是从我正在执行的查询中生成一个缓存键,并将搜索结果与此缓存键保存在地图中。

示例:

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

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

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

所以我要做的是有一个方法缓存:

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. 迭代所有方法并查询参数兼容性

我的猜测:对于大型类(许多方法),第一个方法将获胜,否则第二个方法将获胜

What I usually do when I have to look up methods is to generate a cache key from the query I am doing and save the search result with this cache key in a map.

Example:

I know the method parameters are Boolean.TRUE, Arrays.asList("foo","bar","baz") and BigInteger.valueOf(77777l)

My class contains a method with the signature

public foo(boolean, Collection, Number)

There's no way I can directly map the parameters to the parameter types because I just don't know which of the super classes or interfaces is the parameter type as you can see from the following table:

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

Each of these pairs is compatible, but there's no way to find the compatible method without defining a comparison method, something like this:

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

So what I'd do is have a method cache:

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

and add a lookup method like this:

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();
}

That way, subsequent lookups will take much less time than the first one (for parameters of the same type).

Of course since Class.getDeclaredMethods() uses a cache internally, the question is whether my cache improves performance at all. It's basically a question of what's faster:

  1. generating a cache key and querying a HashMap or
  2. iterating over all methods and querying for parameter compatibility

My guess: for large classes (many methods), the first method will win, otherwise the second will

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文