Class类getDeclaredMethods()相关:单类如何声明同名同参异返回值方法?
先看一下Class.getDeclaredMethods()的文档描述:
返回一个包含{@code Method}对象的数组,这些对象反映此{@code Class}对象所表示的类或接口的所有已声明方法,包括公共、受保护、默认(包)访问和私有方法,但不包括继承的方法。
如果此{@code Class}对象表示的类型有多个名称和参数类型相同但返回类型不同的已声明方法,那么返回的数组将为每个此类方法包含一个{@code Method}对象。
很多人看到这段会疑惑:Java不是规定方法重载得靠方法名+参数列表区分吗?返回类型不算啊?怎么会有单个类里存在这种方法的情况?
其实分两种场景:
1. 编译器自动生成的桥接方法(最常见)
这是你在日常开发中最可能遇到的情况,通常和泛型或者协变返回类型有关。编译器会悄悄帮你生成一个"桥接方法",和你自己写的方法同名、参数列表完全一样,但返回类型不同。
举个协变返回的例子:
class Parent { public Number getValue() { return Integer.valueOf(1); } } class Child extends Parent { @Override public Integer getValue() { // 这里用了协变返回:Integer是Number的子类 return Integer.valueOf(2); } }
编译Child类之后,用javap -c Child反编译就能看到,编译器额外生成了一个桥接方法:
public java.lang.Number getValue(); Code: 0: aload_0 1: invokevirtual #2 // Method getValue:()Ljava/lang/Integer; 4: areturn
这时候调用Child.class.getDeclaredMethods(),就能拿到两个getValue方法:一个是你写的返回Integer的,另一个是编译器生成的返回Number的。
再看泛型的场景:
interface DataProvider<T> { T fetchData(); } class StringProvider implements DataProvider<String> { @Override public String fetchData() { return "Hello World"; } }
编译器会为StringProvider生成一个桥接方法:
public java.lang.Object fetchData(); Code: 0: aload_0 1: invokevirtual #2 // Method fetchData:()Ljava/lang/String; 4: areturn
同样,这个类里现在就有两个fetchData方法,参数列表为空,返回类型分别是String和Object。
2. 手动通过字节码工具生成
Java编译器不允许你直接写这样的方法,但JVM的字节码规范是允许的——字节码里的方法签名是包含返回类型的。所以你可以用ASM、Javassist这类字节码操作库,手动构造一个类,在里面添加两个同名同参数但返回类型不同的方法。
比如用ASM生成的类,字节码里会存在:
public int calculate(); public String calculate();
这种类可以被JVM正常加载,但你没办法用标准Java代码直接调用这两个方法(编译器会因为歧义报错),只能通过反射来调用其中一个。
简单来说:标准Java语法里你不能手动声明这类方法,但编译器会自动生成桥接方法,或者通过字节码工具可以手动创建这样的类。
内容的提问来源于stack exchange,提问作者Pushparaj

