【问题标题】:Code compiling with eclipse but not with javac用 eclipse 编译的代码,但不能用 javac 编译
【发布时间】:2019-10-07 13:33:38
【问题描述】:

下面的代码在 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,我有 错误

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

【问题讨论】:

    标签: java eclipse generics compilation javac


    【解决方案1】:

    这确实是bug in ecj,但其原因要在 JLS 内部找到。

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

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

    字段list具有以下类型:List&lt;A&lt;T&gt;.C&gt;

    要确定字段访问x.list的类型,我们需要查阅x的类型,即A&lt;#capture-of ? extends B&gt;。我们使用它来实例化&lt;T&gt;,生成:List&lt;A&lt;#capture-of ? extends B&gt;&gt;

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

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

    在这种特殊情况下,ecj 没有考虑到的细节是 §4.5.1 只关注直接类型参数,而不是嵌套类型参数。因此,虽然#capture-of ? extends B 包含在? extends B 中,但参数化类型A&lt;#capture-of ? extends B&gt; 只是A&lt;? extends B&gt;子类型,两者都没有包含。由此我们看到List&lt;..&gt;的完整类型确实是不兼容的。

    ecj 正确地遵守了立即类型参数的这些规则,但在分析外部类型 A 的类型参数时感到困惑。

    编辑

    已修复 ecj 错误,可在 https://download.eclipse.org/eclipse/downloads/index.html 获得带有修复的集成构建

    【讨论】:

      【解决方案2】:

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

      javac 行为正确且与JLS 一致:

      如果一个泛型类C&lt;T&gt; 有一个非泛型成员类D,那么 成员类型C&lt;String&gt;.D 是参数化类型,即使类 D 不是通用的。

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

      A<? extends B>.C
      

      parameterized type,其中A的类型参数是Bunknown子类型。因此,关于泛型的所有规则在此上下文中也适用于 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&lt;A&lt;?&gt;.C&gt; 是未知类型的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);
          }
      }
      

      另见:

      【讨论】:

      • 感谢您的回答和解释。我正在等待 Eclipse 对 539105 票的答复。
      • 这个问题已经在 Eclipse 端解决了。
      【解决方案3】:

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

      【讨论】:

      猜你喜欢
      • 2012-11-10
      • 2012-02-14
      • 1970-01-01
      • 2020-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-20
      相关资源
      最近更新 更多