What is the new way of getting all methods of a class, including inherited default methods of Java 8?

后端 未结 2 1621
清歌不尽
清歌不尽 2021-02-07 02:21

I want to get all methods of a class, including public, protected, package and private methods, and including inherited methods.

Remember:

  • Class.ge
2条回答
  •  無奈伤痛
    2021-02-07 02:39

    I was not able to compile Holger's answer in an Android environment since MethodType is added in API level 26 and Android Studio supports a subset of Java 8 Language Features. In addition to this, Holger's code contains to much lambdas and streams, I consider those as human unreadable. So I decided to write a more readable code that works in any Java environment. But it's not an ideal solution since I did not include the flags.

    Below snippets work same as if you called getAllMethods(clazz, false, false)

    private static Collection getAllMethods(Class target) {
        Class clazz = target;
        Collection methodSignatures = new ArrayList<>();
        for(Method method : clazz.getDeclaredMethods()) {
            addIfAbsentAndNonSynthetic(methodSignatures, method);
        }
        for(Method method : clazz.getMethods()) {
            addIfAbsentAndNonSynthetic(methodSignatures, method);
        }
        Package pkg = clazz.getPackage();
        clazz = clazz.getSuperclass();
        while(clazz != null) {
            for(Method method : clazz.getDeclaredMethods()) {
                int modifier = method.getModifiers();
                if(Modifier.isPrivate(modifier)) {
                    continue;
                }
                if(Modifier.isPublic(modifier) || Modifier.isProtected(modifier)) {
                    addIfAbsentAndNonSynthetic(methodSignatures, method);
                }
                else if((pkg != null && pkg.equals(clazz.getPackage())) || (pkg == null
                        && clazz.getPackage() == null)) {
                    addIfAbsentAndNonSynthetic(methodSignatures, method);
                }
            }
            clazz = clazz.getSuperclass();
        }
        Collection allMethods = new ArrayList<>(methodSignatures.size());
        for(MethodSignature methodSignature : methodSignatures) {
            allMethods.add(methodSignature.getMethod());
        }
        return allMethods;
    }
    
    private static void addIfAbsentAndNonSynthetic(Collection collection,
            Method method) {
        MethodSignature methodSignature = new MethodSignature(method);
        if(!method.isSynthetic() && !collection.contains(methodSignature)) {
            collection.add(methodSignature);
        }
    }
    

    Two of the components of a method declaration comprise the method signature: The method's name and the parameter types. The compiler does not consider return type when differentiating methods, so you can not declare two methods with the same signature even if they have a different return type. So MethodSignature class does not hold any reference to the return type of it's method.

    But when you invoke getDeclaredMethods or getMethods it is possible to get multiple declared methods with the same name and parameter types, but different return types. This means that the compiler created a synthetic method, called a bridge method. To solve this problem, invoke method.isSynthetic() on the method, if it returns true skip it. Since it is a synthetic method there will be a non synthetic one with the same signature but different return type.

    public class MethodSignature {
        private final Method mMethod;
        private final String mName;
        private final Class[] mParameterTypes;
    
        public MethodSignature(Method method) {
            mMethod = method;
            mName = mMethod.getName();
            mParameterTypes = mMethod.getParameterTypes();
        }
    
        public Method getMethod() {
            return mMethod;
        }
    
        public String getName() {
            return mName;
        }
    
        public Class[] getParameterTypes() {
            return mParameterTypes;
        }
    
        @Override
        public boolean equals(Object object) {
            if(this == object) {
                return true;
            }
            if(object == null) {
                return false;
            }
            if(!getClass().equals(object.getClass())) {
                return false;
            }
            MethodSignature obj = (MethodSignature) object;
            if(hashCode() != obj.hashCode()) {
                return false;
            }
            return mName.equals(obj.getName()) && Arrays
                    .equals(mParameterTypes, obj.getParameterTypes());
        }
    
        @Override
        public int hashCode() {
            int hash = 11;
            hash = 37 * hash + Objects.hash(mName, Arrays.hashCode(mParameterTypes));
            return hash;
        }
    }
    

提交回复
热议问题