使用 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>
是未知类型的 A
的 C
的列表;任意A
的C
可以加上:
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
获得
下面的代码在 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 classD
, then the member typeC<String>.D
is a parameterized type, even though the classD
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>
是未知类型的 A
的 C
的列表;任意A
的C
可以加上:
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
获得