我有以下代码片段:
class BaseClass {
public Integer getX() {
return 5;
}
}
class InheritedClass extends BaseClass implements Interface {
}
interface Interface {
public Number getX();
}
public class Test5 {
public static void main(String[] args) throws Exception {
System.out.println(InheritedClass.class.getMethod("getX").getReturnType());
}
}
这段代码返回java.lang.Number
,这对我来说很奇怪,因为BaseClass的getX
方法返回java.lang.Integer
。最有趣的是,如果BaseClass
实现接口,返回类型是java.lang.Integer…这是正常的行为吗?
我认为是这样的
通过同时扩展BaseClass
和实现Interface
,子类承诺提供两个getX()
方法:
public Integer getX();
public Number getX();
因为不能重载返回类型,所以只能有一个方法。此外,它的返回类型必须是Number
而不是Integer
(因为可以将后者强制转换为前者,但不能反过来)。
为了协调以上所有内容,编译器在InheritedClass
中自动生成以下方法:
public java.lang.Number getX();
Code:
0: aload_0
1: invokevirtual #22 // Method getX:()Ljava/lang/Integer;
4: areturn
可以看到,它具有Interface
中方法的签名,但自动委托给BaseClass
中的方法(使用隐式向上转换)。
这个自动生成的方法就是你的反射代码所选取的。
是的,这是正常的:
-
Integer
是Number
的子类 - 从Java 5.0开始,Java中的派生类或接口实现方法可以返回父类或接口中声明的返回类型的子类。这被称为返回类型协方差。
- 由于
InheritedClass
实现了Interface
,所以使用该接口的getX
返回类型,即Number
。
JLS的相关部分是§8.4.8.4:
一个类可以继承多个方法覆盖等效签名(§8.4.2)。
如果其中一个继承的方法不是
abstract
…非abstract
的方法被认为是覆盖,因此实现了代表继承它的类的所有其他方法。
这里我们考虑的两种方法是:
-
public Integer getX();
-
public abstract Number getX();
由于BaseClass.getX
不是abstract
,并且它是Interface.getX
的返回类型可替换(Integer
是Number
的子类),因此选择BaseClass.getX
来覆盖Interface.getX
。
这很奇怪,因为这不是你看到的结果。但是,如果您尝试在InheritedClass
中定义方法public Number getX()
,您将收到与上述一致的错误。
这是Java框架1.6中包含泛型的一个函数。Integer和Double是Number的子类型