继承具有冲突的通用超级接口的原始类型

Mik*_*bel 10 java generics intellij-idea

我遇到了一些有趣的Java代码,IntelliJ标记为错误,但javac接受为合法.IntelliJ是错误的,代码是合法的,或者编译器是"错误的",无论是由于错误还是故意放松规则.

我喜欢把我的理解Java类型系统相当不错,和我自己的推理使我怀疑的IntelliJ是错误的,javac是对的.但是,我对JLS有一段时间了,我想知道肯定.

在我们讨论有问题的代码之前,让我们看看一些绝对非法的类似代码:

interface A<T> {}
interface X extends A<String> {}
interface Y extends A<Object> {}

interface Z extends X, Y {} // COMPILE ERROR
Run Code Online (Sandbox Code Playgroud)

正如我所料,IntelliJ都javac 正确地将其标记为错误:'A'不能用不同的类型参数继承:'java.lang.String'和'java.lang.Object'.

没问题.但是如果我们通过扩展原始形式来制作XY通用呢?Z

interface X<T> extends A<String> {}
interface Y<T> extends A<Object> {}

interface Z extends X, Y {}  // OK according to javac, ERROR according to IntelliJ
Run Code Online (Sandbox Code Playgroud)

在这里,IntelliJ急切地报告了它为第一个片段所做的相同错误,但很javac高兴接受所写的代码.

我的理解是原始类型是递归擦除的,这意味着原始类型的所有超类和超接口都被递归擦除的表单替换,依此类推.因此,在有问题的代码中,Z最终A通过两者延伸(原始),X并且Y与第一示例相反,其Z延伸A<String>通过XA<Object>通过Y.

如果确实如此,那么我会得出结论,IntelliJ是错误的,并且javac是正确的:第二个代码片段是合法的.

你怎么说,Stack Overflow的专家?

Jor*_*nee 6

规范在JLS-8.1.5中说:

类可能不是同一通用接口(第9.1.2节)的不同参数化的两种接口类型的子类型,或通用接口的参数化的子类型和命名相同通用接口的原始类型,或发生编译时错误.

请注意,它特别注意"通用接口的参数化的子类型和相同通用接口的原始类型命名"的情况,这将转换为类似的东西interface Z extends A<String>, A {}.没有提到2个原始超级接口的情况.

此外,规范给出了这个例子:

interface I<T> {}
class B implements I<Integer> {}
class C extends B implements I<String> {}
Run Code Online (Sandbox Code Playgroud)

C类导致编译时间错误,因为它试图是两者的亚型I<Integer>I<String>.

问题是,A被扩展为不同类型的参数,如果XY它们都对扩大A<Object>你的第一个片段,还编译.

JLS-4.8说(正如你已经提到的):

原始类型的超类(分别为超级接口)是泛型类型的任何参数化的超类(超接口)的擦除.

这意味着两次Z扩展原始类型A,而不是 A使用不同的参数化.此外,类可以两次具有相同的(间接)超级接口(参见JLS-8.1.5-2中的第二个示例).

所以我得出结论,Intellij在这里是错误的.