java Class.getMethod() 反射和自动装箱的任何解决方案?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1894740/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 18:25:18  来源:igfitidea点击:

Any solution for Class.getMethod() reflection and autoboxing?

javareflection

提问by Daff

I want to use

我想用

Class.getMethod(String name, Class... parameterTypes)

to find the method I need to invoke with the given parameters, but apparently as described in Bug 6176992Java doesn't include autoboxing there. So if my reflected class has a method with a (String, int) signature you still get a NoSuchMethodException with a {String.class, Integer.class} array as a paremeter.

找到我需要使用给定参数调用的方法,但显然如错误 6176992 中所述,Java 不包括自动装箱。所以如果我的反射类有一个带有 (String, int) 签名的方法,你仍然会得到一个带有 {String.class, Integer.class} 数组作为参数的 NoSuchMethodException 。

Is there any sollution for this? The only way I could think of to call getMethod() with every permutation of primitive and non primitive types which I don't really want to do.

有什么解决办法吗?我能想到的唯一方法是用我不想做的原始和非原始类型的每个排列来调用 getMethod() 。

Edit: To make it more clear: I am well aware of the primitive type classes, but I don't see how they could help to solve my problem. My parameterTypes array comes from somewhere and I know that it will only return non primitive types. I can not assume that the interface will only be declared with primitive types and that's exactly my problem:

编辑:为了更清楚:我很清楚原始类型类,但我不明白它们如何帮助解决我的问题。我的 parameterTypes 数组来自某个地方,我知道它只会返回非原始类型。我不能假设接口只会用原始类型声明,这正是我的问题:

public interface TestInterface()
{
    public void doTest(Integer i1, int i2, double d3, Double d);
}

Class<?>[] classes = { Integer.class, Integer.class, Double.class, Double.class }
// Due to autoboxing I should become the doTest method here, but it doesn't work
TestInterface.class.getMethod("doTest", classes);

采纳答案by PSpeed

As @Stephen C mentions, your only hope is to do the search yourself. All of his caveats hold but I'd argue a little flexibility would go a long way to covering most of the gotchas as long as the callers were aware of the caveats... versus making your callers always be painfully specific.

正如@Stephen C 提到的,您唯一的希望是自己进行搜索。他的所有警告都成立,但我认为,只要调用者意识到警告,一点灵活性就可以覆盖大部分问题……而不是让你的调用者总是痛苦地具体。

For code that actually does something like this you can look here: http://meta-jb.svn.sourceforge.net/viewvc/meta-jb/trunk/dev/src/main/java/org/progeeks/util/MethodIndex.java?revision=3811&view=markup

对于实际执行类似操作的代码,您可以在这里查看:http: //meta-jb.svn.sourceforge.net/viewvc/meta-jb/trunk/dev/src/main/java/org/progeeks/util/MethodIndex .java?revision=3811&view=markup

The findMethod() call is the entry point but it delegates (after some caching, etc.) to this method:

findMethod() 调用是入口点,但它委托(在一些缓存等之后)到这个方法:

private Method searchForMethod( String name, Class[] parms ) {
    Method[] methods = type.getMethods();
    for( int i = 0; i < methods.length; i++ ) {
        // Has to be named the same of course.
        if( !methods[i].getName().equals( name ) )
            continue;

        Class[] types = methods[i].getParameterTypes();

        // Does it have the same number of arguments that we're looking for.
        if( types.length != parms.length )
            continue;

        // Check for type compatibility
        if( InspectionUtils.areTypesCompatible( types, parms ) )
            return methods[i];
        }
    return null;
}

InspectionUtils.areTypesCompatible() takes two lists of types, normalizes their primitives, and then verifies that one is "assignable" to the other. So it will handle the case where you have an Integer and are trying to call a method that takes int as well as the case where you have a String and are trying to call a method that takes Object. It does nothandle the case of having an int and calling a method that takes float. There has to be somespecificity.

InspectionUtils.areTypesCompatible() 接受两个类型列表,规范化它们的原语,然后验证一个是否“可分配”给另一个。所以它会处理你有一个整数并试图调用一个接受 int 的方法的情况,以及你有一个 String 并试图调用一个接受 Object 的方法的情况。它并不能处理具有一个int并调用需要浮动的方法的情况下。必须有一些特殊性。

The one caveat is that the above method just searches in method order so if there are ambiguities then the selection is arbitrary. I've never encountered a real-world issue, so far in practice.

一个警告是,上述方法只是按方法顺序搜索,因此如果存在歧义,则选择是任意的。到目前为止,我从未遇到过实际问题。

Here is the compatibility check for reference: public static boolean areTypesCompatible( Class[] targets, Class[] sources ) { if( targets.length != sources.length ) return false;

这是参考的兼容性检查: public static boolean areTypesCompatible( Class[] targets, Class[] sources ) { if( targets.length != sources.length ) return false;

    for( int i = 0; i < targets.length; i++ ) {
        if( sources[i] == null )
            continue;

        if( !translateFromPrimitive( targets[i] ).isAssignableFrom( sources[i] ) )
            return false;
        }
    return( true );
}

The code is BSD and mine so the snippets are legal to use. If you decide you'd rather use this util package directly the most recent public release is here: https://meta-jb.svn.sourceforge.net/svnroot/meta-jb/trunk/dev/m2-repo/org/meta-jb/meta-jb-util/0.17.1/

代码是 BSD 和我的,所以这些片段是合法使用的。如果您决定直接使用这个 util 包,最新的公开版本在这里:https: //meta-jb.svn.sourceforge.net/svnroot/meta-jb/trunk/dev/m2-repo/org/元-jb/元-jb-util/0.17.1/

And I only mention that because there hasn't been a bundled download in a long time since most of my active users are maven users. I seem to be more fond of writing code than cutting full releases. ;)

我之所以提到这一点,是因为我的大多数活跃用户都是 maven 用户,很久没有捆绑下载了。我似乎更喜欢编写代码而不是削减完整版本。;)

回答by Per Olesen

If you have commons-lang with version >=2.5, you can use MethodUtils.getMatchingAccessibleMethod(...) which can handle the boxing types issues.

如果您的 commons-lang 版本 >=2.5,则可以使用 MethodUtils.getMatchingAccessibleMethod(...) 来处理装箱类型问题。

回答by newacct

Yeah you need to use Integer.TYPEor (equivalently) int.class.

是的,您需要使用Integer.TYPEor (等效地)int.class

Update: "My parameterTypes array comes from somewhere and I know that it will only return non primitive types." Well, then that's this "somewhere"'s problem. If they don't give you the proper signature of the method they want, then how are you going to find it? What if there are two overloaded methods, that differ only in one of them takes a primitive and the other one takes the wrapper class? Which one does it choose then? I mean, if you really have no choice then I guess you could just loop through all the methods, and look for one with the right name, and manually check all the parameter types for whether they are correct, or are the primitive equivalents.

更新:“我的 parameterTypes 数组来自某个地方,我知道它只会返回非原始类型。” 那么,这就是“某处”的问题。如果他们没有给你他们想要的方法的正确签名,那么你将如何找到它?如果有两个重载方法,它们的区别仅在于其中一个采用原语,而另一个采用包装类,该怎么办?那它选择哪一个呢?我的意思是,如果你真的别无选择,那么我想你可以循环遍历所有方法,寻找一个具有正确名称的方法,然后手动检查所有参数类型是否正确,或者是原始等价物。

回答by irreputable

reflection method look up is not as sophisticated as compiler's. I understand why you need something like that. suppose at runtime you have a method name, and an array of objects as arguments, and you want reflection to give you the exact method based on types of the arguments, but it's more complicated than that. for example:

反射方法查找不像编译器那样复杂。我明白你为什么需要这样的东西。假设在运行时您有一个方法名称和一个对象数组作为参数,并且您希望反射根据参数类型为您提供确切的方法,但它比这更复杂。例如:

void f(Integer i){..}
void f(int i){...}

when the argument is type Integer, which one can you choose? An even trickier one:

当参数是整数类型时,您可以选择哪一个?一个更棘手的:

void f(Integer i, List l){...}
void f(Object o, LinkedList l){...}

compiler has a set of rules to choose the "most specific method" based on static information; if it can't determine it will alert you right away.

编译器有一套规则可以根据静态信息选择“最具体的方法”;如果它不能确定它会立即提醒您。

you have to simulate the compiler and write an algorithm to find out the "most specific method". (oh, and with consideration of auto-boxing, almost forgot that!)

您必须模拟编译器并编写算法来找出“最具体的方法”。(哦,考虑到自动装箱,差点忘记了!)

回答by Sean Owen

Integer.class represents the Integer object type. Integer.TYPE represents the int primitive type. Does that work?

Integer.class 表示 Integer 对象类型。Integer.TYPE 表示 int 原始类型。那样有用吗?

回答by Stephen C

The only answer at the moment is to write code to simulate the Java compiler's type promotion rules, an reflectively pick the most appropriate method. Autoboxing and unboxing are just examples of the type promotions the compiler knows about ...

目前唯一的答案是编写代码来模拟 Java 编译器的类型提升规则,反思性地选择最合适的方法。自动装箱和拆箱只是编译器知道的类型提升的例子......

Why don't the Java reflective APIs already do this? I can think of a number of reasons.

为什么 Java 反射 API 还没有做到这一点?我能想到很多原因。

  • Before Java 1.5, the getMethodclass and friends did not understand how to do (for example) promotion of intto float. If there wasn't a need pre-1.5, why now?

  • Adding this kind of stuff will make reflective method calling even slower.

  • Autoboxing and unboxing can be confusing with non-reflective method invocation. Adding reflection will only add more confusion.

  • The runtime implementation of the promotion rules will add a class of new runtime error cases that need to be mapped to exceptions and diagnosed by user code. These ones will be particularly tricky. For example, it would have to deal with the reflective equivalent of an ambiguous method call.

  • The overarching requirement for backwards compatibility means that Sun would have to implement this as new methods. They cannot change the behaviors of the current methods because that would potentially break thousands of customers' existing applications.

  • 在 Java 1.5 之前,getMethod班级和小伙伴们不明白如何做(例如)intto 的推广float。如果在 1.5 之前不需要,为什么现在需要?

  • 添加这种东西会使反射方法调用更慢。

  • 自动装箱和拆箱可能会与非反射方法调用混淆。添加反射只会增加更多的混乱。

  • 提升规则的运行时实现将添加一类新的运行时错误案例,需要映射到异常并由用户代码诊断。这些将特别棘手。例如,它必须处理模棱两可的方法调用的反射等价物。

  • 向后兼容性的总体要求意味着 Sun 必须将其作为新方法来实现。他们无法改变当前方法的行为,因为这可能会破坏成千上万客户的现有应用程序。

There is one final point that relates specifically to the OP's use-case (as described). The OP says that his codedoes not know whether to expect (for example) a method with an intor Integerparameter on the target class. Suppose that the person who wrote the target class provided both overloads ... and the semantics are subtly (or unsubtly) different? No matter what you do, there will be situations where the OP's code picks the overload that the client doesn't expect. Bad.

最后一点与 OP 的用例(如所述)特别相关。OP 说他的代码不知道是否期望(例如)目标类上带有intorInteger参数的方法。假设编写目标类的人提供了两个重载......并且语义微妙地(或不微妙地)不同?不管你做什么,都会有 OP 的代码选择客户端不期望的重载的情况。坏的。

IMO, it is better for the OP's code to impose some simple API rules that say when it is correct to use primitives versus wrappers.

IMO,OP 的代码最好强加一些简单的 API 规则,说明何时使用原语与包装器是正确的。

回答by Codebling

Try using Class.isPrimitive()to determine if it a primitive type, then if it is, use reflections to retrieve the TYPEfield and check if that is equal. So, in very liberal pseudocode:

尝试使用Class.isPrimitive()来确定它是否是原始类型,如果是,则使用反射来检索该TYPE字段并检查它是否相等。所以,在非常自由的伪代码中:

for(Method m:getDeclaredMethods())
  for(Class c:m.getParameterTypes() && Class desired:desiredMethodArgTypes)
    if(c.isAssignableFrom(desired))
      //matches
    if(c.isPrimitive() && c==desired.getDeclaredField("TYPE").get(desiredObject))
      //matches

回答by skaffman

You could add some additional logic around your reflection call which tries converting your Integer.class(or whatever) into the corresponding primitive class, and then repeatedly looking up the method until you get a match. If you have Apache Commons Lang, then the wrapperToPrimitivemethod will do this conversation for you, but writing it yourself is trivial.

您可以在反射调用周围添加一些额外的逻辑,尝试将您的Integer.class(或其他)转换为相应的原始类,然后重复查找该方法,直到获得匹配项。如果您有 Apache Commons Lang,那么wrapperToPrimitive方法将为您完成这个对话,但自己编写它是微不足道的。

  1. Perform getMethodas usual
  2. If nothing found, then look for any parameter types which have corresponding primitives
  3. For each combination of those, perform another lookup until something sticks.
  1. getMethod照常执行
  2. 如果没有找到,则查找具有相应原语的任何参数类型
  3. 对于这些的每个组合,执行另一次查找,直到某些东西粘住为止。

Not elegant, and for methods with a lot of primitive parameters, it might even be slow.

不优雅,对于具有大量原始参数的方法,它甚至可能很慢。