您必须小心对已编译类成员的假设。
甚至还有编译器生成的成员,它们是可访问 API 的一部分,例如默认构造函数或 enum 类型的 values() 和 valueOf(String) 方法。此外,内部类和枚举类型的编译构造函数可能具有比源代码中可见的更多参数,并且由于类型擦除,编译方法中的签名可能与源代码不同。
除此之外,还可以有不同的合成成员。从 Java 1.1 到 Java 10,嵌套类可以通过合成辅助方法(Java 11 已经过时)访问彼此的私有成员。此外,覆盖泛型类的方法或使用协变返回类型可能会导致生成合成桥方法。
这还不是全部。
下面的程序
import java.util.Arrays;
import java.util.stream.Stream;
public enum ShowSyntheticMembers {
;
public static void main(String[] args) {
Stream.of(ShowSyntheticMembers.class, Inner.class)
.flatMap(cl -> Stream.concat(Arrays.stream(cl.getDeclaredFields()),
Arrays.stream(cl.getDeclaredMethods())))
.forEach(System.out::println);
}
private boolean x;
class Inner {
protected String clone() {
assert x;
return "";
}
}
}
使用 JDK 11 编译时打印:
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
private static void ShowSyntheticMembers.lambda$main$1(java.io.PrintStream,java.lang.Object)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
在使用 JDK 8 编译和运行时产生
private boolean ShowSyntheticMembers.x
private static final ShowSyntheticMembers[] ShowSyntheticMembers.$VALUES
public static void ShowSyntheticMembers.main(java.lang.String[])
public static ShowSyntheticMembers[] ShowSyntheticMembers.values()
public static ShowSyntheticMembers ShowSyntheticMembers.valueOf(java.lang.String)
static boolean ShowSyntheticMembers.access$000(ShowSyntheticMembers)
private static java.util.stream.Stream ShowSyntheticMembers.lambda$main$0(java.lang.Class)
static final boolean ShowSyntheticMembers$Inner.$assertionsDisabled
final ShowSyntheticMembers ShowSyntheticMembers$Inner.this$0
protected java.lang.String ShowSyntheticMembers$Inner.clone()
protected java.lang.Object ShowSyntheticMembers$Inner.clone() throws java.lang.CloneNotSupportedException
-
$VALUES 是编译器生成的 values() 实现的工件。
-
$assertionsDisabled 部分执行 assert 语句。
-
this$0 是内部类对其外部this 的隐式引用。
- 返回类型为
Object 的clone() 方法是一种桥接方法。
-
access$000 方法有助于从内部类访问外部类的 private 字段,这在 JDK 11 之前是必需的。
- 有趣的是,合成方法
lambda$main$1,只存在于JDK 11编译版本中,是System.out::println方法参考的一部分,但这里实际上不需要。
这是修复某些交集类型相关问题的副作用,因此非常特定于编译器。将源代码中的 .flatMap(…) 更改为 .<Object>flatMap(…) 会使该方法消失,即使使用此特定编译器版本。
因此,由于许多因素决定了源代码中不可见的合成成员的存在,因此您不应仅使用参数类型作为条件来搜索特定方法。
当您想访问public 成员时,最好使用Handler.class.getMethods() 而不是Handler.class.getDeclaredMethods()。或者使用Handler.class.getMethod("handle", Bar.class)直接获取想要的方法。
如果您不想将方法名称硬编码为字符串,则运行时可见注释可以帮助识别正确的方法。