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

后端 未结 8 1047
一向
一向 2021-02-12 22:16

I want to use

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

to find the method I need to invoke with the given parameters, but apparent

相关标签:
8条回答
  • 2021-02-12 22:47

    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.

    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

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

    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 not handle the case of having an int and calling a method that takes float. There has to be some specificity.

    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;

        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/

    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. ;)

    0 讨论(0)
  • 2021-02-12 22:52

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

    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
    
    0 讨论(0)
  • 2021-02-12 22:54

    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 wrapperToPrimitive method will do this conversation for you, but writing it yourself is trivial.

    1. Perform getMethod as 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.

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

    0 讨论(0)
  • 2021-02-12 22:58

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

    0 讨论(0)
  • 2021-02-12 22:59

    Yeah you need to use Integer.TYPE or (equivalently) 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.

    0 讨论(0)
  • 2021-02-12 23:00

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

    0 讨论(0)
提交回复
热议问题