获取类的所有方法的新方法是什么,包括Java 8的继承默认方法? [英] What is the new way of getting all methods of a class, including inherited default methods of Java 8?

查看:90
本文介绍了获取类的所有方法的新方法是什么,包括Java 8的继承默认方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想获取类的所有方法,包括public,protected,package和private方法,以及包括继承的方法。

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

请记住:


  • Class.getDeclaredMethods() 获取公共,受保护,包和私有
    方法,排除继承方法。

  • Class.getMethods 获取继承的方法,仅限公共方法。

  • Class.getDeclaredMethods() gets public, protected, package and private methods, but excludes inherited methods.
  • Class.getMethods gets inherited methods, but only the public ones.

在Java 8之前,我们可以采取以下措施:

Before Java 8 we could do something along the lines of:

Collection<Method> found = new ArrayList<Method>();
while (clazz != null) {
    for (Method m1 : clazz.getDeclaredMethods()) {
        boolean overridden = false;

        for (Method m2 : found) {
            if (m2.getName().equals(m1.getName())
              && Arrays.deepEquals(m1.getParameterTypes(), m2
                  .getParameterTypes())) {
            overridden = true;
            break;
            }
        }
        if (!overridden) found.add(m1);
    }

    clazz = clazz.getSuperclass();
}
return found;

但现在,如果该类使用默认方法实现某些界面,那么没有被具体超类覆盖,这些方法将逃避上述检测。此外,现在有关于同名默认方法的规则,这些规则也必须考虑在内。

But now, if the class implements some interface with default methods which are not overridden by concrete superclasses, these methods will escape the above detection. Besides, there are now rules concerning default methods with the same name, and these rules must be taken into account as well.

问题:什么是目前推荐的获取类的所有方法的方法:

Question: What is the current recommended way of getting all methods of a class:

all的最常见定义应该是可以使用的方法直接访问类的实例方法,而不使用 super 或类名:

The most common definition of "all" should be the methods that can be directly accessed inside an instance method of the class, without the use of super or class names:


  • 包括在类本身中声明的public,protected,package和private方法。

  • 包括其超类的受保护方法。

  • 包含包相同包的超类的方法。

  • 包括其接口的默认方法(未覆盖/隐藏的方法,请参阅这里这里)。

  • 包含具有适当可访问性的静态方法(类和超类)。

  • 不包括私有超类方法。

  • 不要包含重写方法。

  • 不包含隐藏方法(特殊情况下,不包括隐藏的静态方法)。 / li>
  • 不包括合成/桥接方法。

  • 不包括Java不允许的方法,即使JVM允许这些方法。

  • Include public, protected, package and private methods declared in the class itself.
  • Include protected methods of its superclasses.
  • Include package methods of its superclasses of the same package.
  • Include default methods of its interfaces (those not overridden/hidden, see here and here).
  • Include static methods (class and superclasses) with the appropriate accessibility.
  • Don't include private methods of superclasses.
  • Don't include overridden methods.
  • Don't include hidden methods (in special, don't include hidden static methods).
  • Don't include synthetic/bridge methods.
  • Don't include methods not allowed by Java, even if the JVM allows them.

因此,当两个布尔标志都是 false 时,上面的定义符合以下签名:

So, the above definition fits the following signature when both boolean flags are false:

public Collection<Method> getAllMethods(Class clazz,
                               boolean includeAllPackageAndPrivateMethodsOfSuperclasses,
                               boolean includeOverridenAndHidden)

理想情况,规范的答案,应该允许这些布尔标志。

The ideal, canonical answer, should allow for these boolean flags.

推荐答案

即使对于之前的Java 8场景,你的代码片段也不是不对。但是,收集所有方法并不是一种常见的方案,因为您通常需要有关特定上下文的方法,例如:您可能想知道哪些方法可访问给定的上下文,其中不包括所有方法,即使您考虑非 public 方法。如果你真的想要所有方法,你必须回想一下 private static 方法是从不覆盖,并且只有在相同的中声明时,才会覆盖package-private方法。因此,过滤每个遇到的方法签名是不正确的。

Even for the "Before Java 8" scenario, your code snippet isn’t correct. But collecting all methods isn’t a usual scenario though, as you normally need methods regarding a certain context, e.g. you might want to know which methods are accessible for a given context, which doesn’t include all methods, even if you consider non-public methods. If you really want all methods, you have to recall that private and static methods are never overridden and package-private methods are only overridden when being declared within the same package. So it’s not correct to filter every encountered method signature.

更糟糕的是,方法可能会被不同的修饰符覆盖。后者可以通过保持想法从实际类开始并使用 Class.getMethods()来获得所有 public 方法包括默认方法并遍历超类层次结构,朝向 java.lang.Object ,因此已经遇到的覆盖具有限制性最小的访问修饰符。

What makes matters worse is that methods might get overridden with different modifiers. The latter can be solved by keeping the idea to start at the actual class and use Class.getMethods() to get all public method including default methods and traverse the superclass hierarchy towards java.lang.Object so the already encountered overrides have the least restrictive access modifiers.

作为旁注,嵌套线性搜索循环绝不是一个好主意。你很快就会得到二次或更差的复杂性。

As a side note, nesting linear search loops is never a good idea. You’ll soon end up with a quadratic or worse complexity.

你可以使用以下方法收集方法:

You may collect methods using the following method:

public static Set<Method> getAllMethods(Class<?> cl) {
    Set<Method> methods=new LinkedHashSet<>();
    Collections.addAll(methods, cl.getMethods());
    Map<Object,Set<Package>> types=new HashMap<>();
    final Set<Package> pkgIndependent = Collections.emptySet();
    for(Method m: methods) types.put(methodKey(m), pkgIndependent);
    for(Class<?> current=cl; current!=null; current=current.getSuperclass()) {
        for(Method m: current.getDeclaredMethods()) {
            final int mod = m.getModifiers(),
                access=Modifier.PUBLIC|Modifier.PROTECTED|Modifier.PRIVATE;
            if(!Modifier.isStatic(mod)) switch(mod&access) {
                case Modifier.PUBLIC: continue;
                default:
                    Set<Package> pkg=
                        types.computeIfAbsent(methodKey(m), key -> new HashSet<>());
                    if(pkg!=pkgIndependent && pkg.add(current.getPackage())) break;
                    else continue;
                case Modifier.PROTECTED:
                    if(types.putIfAbsent(methodKey(m), pkgIndependent)!=null) continue;
                    // otherwise fall-through
                case Modifier.PRIVATE:
            }
            methods.add(m);
        }
    }
    return methods;
}

private static Object methodKey(Method m) {
    return Arrays.asList(m.getName(),
        MethodType.methodType(m.getReturnType(), m.getParameterTypes()));
}

但如上所述,可能是因为它不适合无论你想做什么。您应首先问自己以下问题:

But as said, it might be the case that it isn’t suitable for whatever you want to do. You should ask yourself the following questions first:


  • 您是否正在寻找构成API的方法(通常是公共仅受保护

  • 或者您是否希望实际看到某些 class / package context?

  • 应该 static 包含哪些方法?

  • 是否应包含合成/桥接方法?

  • 等。

  • Are you looking for methods that make up the API (that’s usually public and protected only)?
  • Or do you want to actually see methods accessible for a certain class/package context?
  • Shall static methods be included?
  • Shall synthetic/bridge methods be included?
  • etc.

以下是适用于您更具体要求的修订方法:

Here is the revised method adapted to your more specific request:

public static Collection<Method> getAllMethods(Class clazz,
                boolean includeAllPackageAndPrivateMethodsOfSuperclasses,
                boolean includeOverridenAndHidden) {

    Predicate<Method> include = m -> !m.isBridge() && !m.isSynthetic() &&
         Character.isJavaIdentifierStart(m.getName().charAt(0))
      && m.getName().chars().skip(1).allMatch(Character::isJavaIdentifierPart);

    Set<Method> methods = new LinkedHashSet<>();
    Collections.addAll(methods, clazz.getMethods());
    methods.removeIf(include.negate());
    Stream.of(clazz.getDeclaredMethods()).filter(include).forEach(methods::add);

    final int access=Modifier.PUBLIC|Modifier.PROTECTED|Modifier.PRIVATE;

    Package p = clazz.getPackage();
    if(!includeAllPackageAndPrivateMethodsOfSuperclasses) {
        int pass = includeOverridenAndHidden?
            Modifier.PUBLIC|Modifier.PROTECTED: Modifier.PROTECTED;
        include = include.and(m -> { int mod = m.getModifiers();
            return (mod&pass)!=0
                || (mod&access)==0 && m.getDeclaringClass().getPackage()==p;
        });
    }
    if(!includeOverridenAndHidden) {
        Map<Object,Set<Package>> types = new HashMap<>();
        final Set<Package> pkgIndependent = Collections.emptySet();
        for(Method m: methods) {
            int acc=m.getModifiers()&access;
            if(acc==Modifier.PRIVATE) continue;
            if(acc!=0) types.put(methodKey(m), pkgIndependent);
            else types.computeIfAbsent(methodKey(m),x->new HashSet<>()).add(p);
        }
        include = include.and(m -> { int acc = m.getModifiers()&access;
            return acc!=0? acc==Modifier.PRIVATE
                    || types.putIfAbsent(methodKey(m), pkgIndependent)==null:
                noPkgOverride(m, types, pkgIndependent);
        });
    }
    for(clazz=clazz.getSuperclass(); clazz!=null; clazz=clazz.getSuperclass())
        Stream.of(clazz.getDeclaredMethods()).filter(include).forEach(methods::add);
    return methods;
}
static boolean noPkgOverride(
        Method m, Map<Object,Set<Package>> types, Set<Package> pkgIndependent) {
    Set<Package> pkg = types.computeIfAbsent(methodKey(m), key -> new HashSet<>());
    return pkg!=pkgIndependent && pkg.add(m.getDeclaringClass().getPackage());
}
private static Object methodKey(Method m) {
    return Arrays.asList(m.getName(),
        MethodType.methodType(m.getReturnType(), m.getParameterTypes()));
}

这篇关于获取类的所有方法的新方法是什么,包括Java 8的继承默认方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆