Java自动返回类型协方差与泛型子类

Nic*_*ery 12 java generics polymorphism subclass covariance

我有两个看起来像这样的接口:

interface Parent<T extends Number> {
    T foo();
}

interface Child<T extends Integer> extends Parent<T> {
}
Run Code Online (Sandbox Code Playgroud)

如果我有一个原始Parent对象,则调用foo()默认值返回a,Number因为没有类型参数.

Parent parent = getRawParent();
Number result = parent.foo(); // the compiler knows this returns a Number
Run Code Online (Sandbox Code Playgroud)

这是有道理的.

如果我有一个原始Child对象,我希望调用foo()将返回一个Integer相同的逻辑.但是,编译器声称它返回一个Number.

Child child = getRawChild();
Integer result = child.foo(); // compiler error; foo() returns a Number, not an Integer
Run Code Online (Sandbox Code Playgroud)

我可以覆盖Parent.foo()Child解决这个问题,就像这样:

interface Child<T extends Integer> extends Parent<T> {
    @Override
    T foo(); // compiler would now default to returning an Integer
}
Run Code Online (Sandbox Code Playgroud)

为什么会这样?有没有办法让Child.foo()默认返回Integer没有覆盖Parent.foo()

编辑:假装Integer不是最终的.我刚刚选择NumberInteger作为例子,但显然它们不是最好的选择.:S

fuk*_*hik 5

  1. 这是基于@AdamGent的想法.
  2. 不幸的是,我不熟悉JLS足以证明以下规范.

Imagine public interface Parent<T extends Number>是在另一个编译单元中定义的- 在一个单独的文件中Parent.java.

然后,编译时Childmain,编译器会看到方法fooNumber foo().证明:

import java.lang.reflect.Method;
interface Parent<T extends Number> {
    T foo();
}

interface Child<R extends Integer> extends Parent<R> {
}

public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println(Child.class.getMethod("foo").getReturnType());
    }
}
Run Code Online (Sandbox Code Playgroud)

打印:

class java.lang.Number
Run Code Online (Sandbox Code Playgroud)

该输出作为合理Java那样类型擦除,不能够保留T extends在结果.class文件,因为方法foo()是只定义了Parent.要更改子编译器中的结果类型,需要在字节码中插入存根 Integer foo()方法Child.class.这是因为编译后仍然没有关于泛型类型的信息.

现在,如果你修改你的孩子:

interface Child<R extends Integer> extends Parent<R> {
    @Override R foo();
}
Run Code Online (Sandbox Code Playgroud)

例如,将自己添加foo()Child编译器中将使用不同但仍兼容的原型Child.class文件中创建自己的方法副本Integer foo().现在输出是:

class java.lang.Integer
Run Code Online (Sandbox Code Playgroud)

这当然令人困惑,因为人们会期望"词汇可见性"而不是"字节码可见性".

另一种方法是编译器在两种情况下以不同的方式编译它:接口在相同的"词法范围"中,当编译器只能看到字节码时,编译器可以在不同的编译单元中看到源代码和接口.我不认为这是一个很好的选择.