java 如何使用反射获取参数类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4967575/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get parameter types using reflection?
提问by Abhinav
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?
我想使用具有不同数量参数的函数。问题是我不知道每个函数的参数数量,也不知道函数的名称,因为它们存储在数组中。我只知道类名,但不想使用,getDeclaredMethods
因为它会增加搜索时间。有没有办法获取每个函数的参数类型?
回答by Sean Patrick Floyd
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)
我知道方法参数是Boolean.TRUE
,Arrays.asList("foo","bar","baz")
和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:
当然,由于Class.getDeclaredMethods()
在内部使用缓存,问题是我的缓存是否完全提高了性能。这基本上是什么更快的问题:
- generating a cache key and querying a HashMap or
- iterating over all methods and querying for parameter compatibility
- 生成缓存键并查询 HashMap 或
- 迭代所有方法并查询参数兼容性
My guess: for large classes (many methods), the first method will win, otherwise the second will
我的猜测:对于大类(许多方法),第一种方法会赢,否则第二种方法会