我想获取一个类的所有方法,包括public、protected、package和private方法,也包括继承的方法。
记住:
Class.getDeclaredMethods()
获取 public、protected、package 和 private 方法,但不包括继承的方法。Class.getMethods
获取继承方法,但仅限公共(public)方法。
在 Java 8 之前,我们可以按照以下方式做一些事情:
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;
但是现在,如果该类实现了一些具有未被具体父类(super class)覆盖的默认方法的接口(interface),这些方法将逃避上述检测。此外,现在有关于同名默认方法的规则,这些规则也必须考虑在内。
问题:目前推荐的获取类所有方法的方法是什么:
“all”最常见的定义应该是可以在类的实例方法内部直接访问的方法,无需使用 super
或类名:
- 包括在类本身中声明的公共(public)、 protected 、包和私有(private)方法。
- 包括其父类(super class)的 protected 方法。
- 包括同一包的父类(super class)的包方法。
- 包括其接口(interface)的默认方法(那些未被覆盖/隐藏的方法,参见 here 和 here)。
- 包括具有适当可访问性的静态方法(类和父类(super class))。
- 不要包含父类(super class)的私有(private)方法。
- 不要包含重写的方法。
- 不包含隐藏方法(特别是不包含隐藏静态方法)。
- 不包括合成/桥接方法。
- 不要包含 Java 不允许的方法,即使 JVM 允许它们也是如此。
因此,当两个 boolean 标志均为 false
时,上述定义符合以下签名:
public Collection<Method> getAllMethods(Class clazz,
boolean includeAllPackageAndPrivateMethodsOfSuperclasses,
boolean includeOverridenAndHidden)
理想的、规范的答案应该允许这些 boolean 标志。
最佳答案
即使对于“Java 8 之前”的场景,您的代码片段也不正确。但是收集所有方法并不是一个常见的场景,因为您通常需要关于特定上下文的方法,例如您可能想知道对于给定上下文哪些方法是可访问 的,这并不包括所有方法,即使您考虑了非公共(public)
方法也是如此。如果你真的想要 all 方法,你必须记住 private
和 static
方法永远不会被覆盖,而 package-private 方法只有在被覆盖时才会被覆盖在同一 package
中声明。所以过滤每一个遇到的方法签名是不正确的。
更糟糕的是,方法可能会被不同的修饰符覆盖。后者可以通过保持从实际类开始的想法来解决,并使用 Class.getMethods()
获取所有 public
方法,包括 default
方法并将父类(super class)层次结构遍历到 java.lang.Object
,因此已经遇到的覆盖具有最少限制的访问修饰符。
作为旁注,嵌套线性搜索循环从来都不是一个好主意。您很快就会遇到二次或更糟的复杂性。
您可以使用以下方法收集方法:
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()));
}
但如前所述,它可能不适合你想做的任何事情。您应该首先问自己以下问题:
- 您是否正在寻找构成 API 的方法(通常是
public
和protected
)? - 或者您是否想实际查看特定
类
/包
上下文可访问的方法? - 是否应包含
static
方法? - 是否应包括合成/桥接方法?
- 等等
这是根据您更具体的要求修改后的方法:
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 - 获取类的所有方法的新方法是什么,包括 Java 8 的继承默认方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28400408/