Java泛型,类型丢失

时间:2018-12-28 23:05:09

标签: java eclipse generics compiler-errors

以下示例虽然看似正确,但无法编译(Eclipse Neon 3,Java 1.8):

class Test {    

    public static class SomeForm<IF extends SomeForm<IF>> {

    }

    public static class BaseFF<IF extends SomeForm<IF>> {

    }

    public static class AuxFF<IF extends SomeForm<IF>>
            extends BaseFF<IF> {

    }

    public interface Interface<IF extends SomeForm<IF>, FF extends BaseFF<IF>> {
        FF getFF1();        
    }

    public static class ZBaseUnit<IF extends SomeForm<IF>, FF extends BaseFF<IF>>
            implements Interface<IF, FF> {

        @Override
        public FF getFF1() {
            return null;
        }

    }

    public static class ZMyUnit<IF extends SomeForm<IF>, FF extends AuxFF<IF>>
            extends ZBaseUnit<IF, FF> {

    }

    public static class ZMyCheck<IF extends SomeForm<IF>, U extends ZMyUnit<IF, ?>> {
        U unit;

        void f() {
            BaseFF<IF> ff1 = unit.getFF1();
        }

    }

}

Eclipse说(在f()方法内的行上):

“类型不匹配:无法从?的Capture#2转换为Test.BaseFF”。

但是,如果我从接口Interface(以及类ZBaseUnit中的@Override批注)中删除方法getFF1,它将进行编译。这背后有逻辑吗?从直觉上看,传递给Interface的FF与传递给ZBaseUnit的FF相同,因此应该没有区别...

此外,如果我在ZMyUnit中添加方法,也不会出错:

void f() {
    BaseFF<IF> ff1 = getFF1();
}

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:2)

如果您将此验证码添加到您的课程中

public static class GF extends SomeForm<GF> {
}

public static void main(String[] args) {
    ZMyCheck<GF, ZMyUnit<GF, AuxFF<GF>>> z = new ZMyCheck<>();
    z.unit = new ZMyUnit<>();
    z.f();
    System.out.println("OK");
}

并使用标准的JDK命令行工具(javacjava),则代码将编译并成功运行。如果您使用NetBeans,也是如此(我无意以任何方式升级NetBeans)。

因此,问题是特定于Eclipse的。 Eclipse有自己的内置编译器,您似乎已经发现了它的弱点之一(也许您需要提交错误报告)。实际上,无法确定通配符?是指扩展AuxFF<IF>的类型(因此也就是BaseFF<IF>)。您需要明确指出:

public static class ZMyCheck<IF extends SomeForm<IF>,
    U extends ZMyUnit<IF, ? extends AuxFF<IF>>> {...}

然后所有程序都将进行编译和运行。

答案 1 :(得分:1)

关于类ZMyCheck和...中的变量初始化...

    public static class ZMyCheck<IF extends SomeForm<IF>, U extends ZMyUnit<IF, ?>> {
        U unit;

        void f() {
            BaseFF<IF> ff1 = unit.getFF1();
        }

    }

...正确性最终取决于表达式unit.getFF1()的类型,以及该类型是否为BaseFF<IF>的子类型。该表达式的类型是实例变量unit类型的函数,由于类型擦除,该变量被解释为类型变量U的上限,即{{1 }}。

关于ZMyUnit<IF, ?>的类型,则JLS指定

  

[类型]的字段,方法和构造函数的类型是[该类型]的捕获转换中的字段,方法和构造函数的类型。

JLS 10, 4.5.2

因此,我们需要确定section 5.1.10中指定的unit.getFF1()类型的捕获转换。规范在这里变得相当技术性,但是最重要的是,ZMyUnit<IF, ?>的第二种类型参数的上限已计入,因此存在对类型ZMyUnit的捕获转换。根据该类型的解释,方法ZMyUnit<IF, AuxFF<IF>>返回类型getFF1(),这需要经过扩展引用转换为类型AuxFF<IF>,这是需要的。

因此,正如@JohnMcClane也回答的那样,Eclipse的编译器在拒绝显示的代码方面是不正确的。在通配符上添加适当的上限似乎可以解决此问题,而无需更改有关JLS的代码语义。