使用 eclipse 编译代码但不使用 javac

Code compiling with eclipse but not with javac

下面的代码在 Eclipse 中编译没有任何错误,但在 Javac 中生成错误。好像是编译错误,不知道哪个是对的

我想指出的是,我知道如何通过更改代码使其同时适用于两者来更正此错误,但这不是当前主题。我只想知道这是 java 还是 eclipse 问题。

我尝试使用 Intellij,但出现相同的 javac 错误。

重现此错误的示例代码:

import java.util.ArrayList;
import java.util.List;

public class A<T extends B> {
    protected List<C> list = new ArrayList<>();

    class C {}

    public void createIO() {
        A<? extends B> x = null;
        List<A<? extends B>.C> y = x.list;
    }
}

class B {
}

JVM:

openjdk version "13-BellSoft" 2019-09-17
OpenJDK Runtime Environment (build 13-BellSoft+33)
OpenJDK 64-Bit Server VM (build 13-BellSoft+33, mixed mode, sharing)

使用 Eclipse,我没有任何错误。使用 Javac,我有 error:

A.java:13: error: incompatible types: List<A<CAP#1>.C> cannot be converted to List<A<? extends B>.C>
        List<A<? extends B>.C> y = x.list;
                                    ^
  where CAP#1 is a fresh type-variable:
    CAP#1 extends B from capture of ? extends B

可能是 Idea 和 Eclipse 使用了他们自己的编译器。

Eclipse 编译器应该像 javac 一样显示编译器错误。参见 Eclipse bug 539105

javac 行为是正确的并且与 JLS:

一致

And if a generic class C<T> has a non-generic member class D, then the member type C<String>.D is a parameterized type, even though the class D is not generic.

因此,即使内部 class C 不是通用的,但:

A<? extends B>.C

是一个 parameterized type,其中 A 的类型参数是 B未知 子类型。因此,在这种情况下,泛型的所有规则也适用于 C

因此,为了消除编译器错误,y 的类型可以声明如下:

List<? extends A<? extends B>.C> y = x.list;

但请记住,这不允许您向列表中添加项目 y(参见 PECS)。

如果听起来很复杂,请尝试在简化示例的范围内考虑它:

List<Integer> l1 = new ArrayList<>();
List<Number > l2 = l1; // Error: incompatible types...

List<? extends Number> l3 = l1; // OK

其他详细信息:

问题中的例子可以简化为:

class A<T> {
    class C {}
    List<C> l1 = null;
    List<A<?>.C> l2 = l1; // Error: incompatible types...
    List<? extends A<?>.C> l3 = l1; // OK
}

List<A<?>.C> 是未知类型的 AC 的列表;任意AC可以加上:

class A<T> {
    class C {}
    C c = new C();
    void foo(List<A<?>.C> list) {
        list.add(new A<String>().c);
        list.add(new A<Number>().c);
    }
}

另请参阅:

  • Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?

这确实是一个 bug in ecj,但其原因可以在 JLS 的深处找到。

为了理解这个问题,我们首先需要看到 C 确实是一个泛型类型,因为它是在类型变量 <T> 的范围内声明的。在这里我同意另一个答案。 (请注意,如果 C 被声明为 static,这将不适用)。

下一步让我们扩展源代码中缩写的一些类型:

字段 list 具有以下类型:List<A<T>.C>

要确定字段访问的类型x.list,我们需要查询x的类型,即A<#capture-of ? extends B>。我们用它来实例化 <T>,它产生:List<A<#capture-of ? extends B>>.

后一种类型看起来与所需类型非常相似 List<A<? extends B>>,未知类型(捕获或通配符)出现在左右完全相同的位置。

下一个需要解释的概念是 JLS §4.5.1 中定义的类型参数包含,这是具有不同类型参数的两个参数化类型之间兼容的必要条件。

在这种特殊情况下 ecj 没有考虑到的细节是,§4.5.1 只关注直接类型参数,而不是嵌套类型参数。因此,虽然 #capture-of ? extends B 包含在 ? extends B 中,但参数化类型 A<#capture-of ? extends B> 只是 A<? extends B> 子类型 ,[=54] 也不是=]包含在另一个。由此可见,完整类型 List<..> 确实不兼容。

ecj 正确地遵守了直接类型参数的这些规则,但在分析外部类型 A.

的类型参数时感到困惑

编辑:

ecj 错误已修复,包含修复的集成版本可在 https://download.eclipse.org/eclipse/downloads/index.html

获得