下界通配符会导致 javac 出现问题,但不会导致 Eclipse [英] Lower-bounded wild card causes trouble in javac, but not Eclipse
问题描述
这段代码可以在 Eclipse 中编译,但不能在 javac 中编译:
This piece of code compiles in Eclipse but not in javac:
import java.util.function.Consumer;
public class Test {
public static final void m1(Consumer<?> c) {
m2(c);
}
private static final <T> void m2(Consumer<? super T> c) {
}
}
javac 输出:
C:Userslukasworkspace>javac -version
javac 1.8.0_92
C:Userslukasworkspace>javac Test.java
Test.java:5: error: method m2 in class Test cannot be applied to given types;
m2(c);
^
required: Consumer<? super T>
found: Consumer<CAP#1>
reason: cannot infer type-variable(s) T
(argument mismatch; Consumer<CAP#1> cannot be converted to Consumer<? super T>)
where T is a type-variable:
T extends Object declared in method <T>m2(Consumer<? super T>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
1 error
----------------------------------------------------------------
哪个编译器是错误的,为什么?(此处的 Eclipse 错误报告和部分讨论)
Which compiler is wrong and why? (Eclipse bug report and parts of discussion here)
推荐答案
此代码在 JLS 8 中是合法的.javac 8 版及更早版本在处理通配符和捕获的方式上存在一些错误.从版本 9(抢先体验,我尝试了 ea-113 和更新版本)开始,javac 也接受此代码.
This code is legal wrt JLS 8. javac version 8 and earlier had several bugs in how they handle wildcards and captures. Starting with version 9 (early access, I tried version ea-113 and newer) also javac accepts this code.
要了解编译器如何根据 JLS 进行分析,必须区分通配符捕获、类型变量、推理变量等.
To understand how a compiler analyzes this according to JLS, it is essential to tell apart what are wildcard captures, type variables, inference variables and such.
c
的类型是 Consumer
(javac 会写成 Consumer
).此类型未知,但已修复.
The type of c
is Consumer<capture#1-of ?>
(javac would write Consumer<CAP#1>
). This type is unknown, but fixed.
m2
的参数类型为Consumer
,其中T
是一个类型变量,需要通过类型推断来实例化.
The parameter of m2
has type Consumer<? super T>
, where T
is a type variable to be instantiated by type inference.
在类型推断期间,一个推断变量,由ecj表示为T#0
,用于表示T
.
During type inference an inference variable, denoted by ecj as T#0
, is used to represent T
.
类型推断在于确定是否可以将 T#0
实例化为任何类型而不违反任何给定的类型约束.在这种特殊情况下,我们从以下约束开始:
Type inference consists in determining, whether T#0
can be instantiated to any type without violating any given type constraints. In this particular case we start with this contraint:
⟨c → 消费者⟩
⟨c → Consumer<? super T#0>⟩
逐步减少(通过应用 JLS 18.2):
Which is stepwise reduced (by applying JLS 18.2):
⟨Consumer
⟨Consumer<capture#1-of ?> → Consumer<? super T#0>⟩
⟨capture#1-of ?<= ?超级T#0⟩
⟨capture#1-of ? <= ? super T#0⟩
⟨T#0 <: capture#1-of ?⟩
⟨T#0 <: capture#1-of ?⟩
T#0 <: capture#1-of ?
T#0 <: capture#1-of ?
最后一行是类型绑定";并减少了.由于不涉及进一步的约束,解析简单地将 T#0
实例化为 capture#1-of ?
类型.
The last line is a "type bound" and reduction is done. Since no further constraints are involved, resolution trivially instantiates T#0
to the type capture#1-of ?
.
通过这些步骤,类型推断已经证明 m2
适用于这个特定的调用.qed.
By these steps, type inference has proven that m2
is applicable for this particular invocation. qed.
直观地,显示的解决方案告诉我们:无论捕获可能表示什么类型,如果 T
设置为表示完全相同的类型,则不会违反任何类型约束.这是可能的,因为捕获在开始类型推断之前 是固定的.
Intuitively, the shown solution tells us: whatever type the capture may represent, if T
is set to represent the exact same type, no type constraints are violated. This is possible, because the capture is fixed before starting type inference.
这篇关于下界通配符会导致 javac 出现问题,但不会导致 Eclipse的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!