Luk*_*der 17 java eclipse generics javac java-8
这段代码在Eclipse中编译,但不在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) {
}
}
Run Code Online (Sandbox Code Playgroud)
javac输出:
C:\Users\lukas\workspace>javac -version
javac 1.8.0_92
C:\Users\lukas\workspace>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
----------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
哪个编译器错了,为什么?(Eclipse错误报告和部分讨论在这里)
Ste*_*ann 11
这段代码与JLS 8合法.javac版本8及更早版本在如何处理通配符和捕获方面存在一些错误.从版本9(早期访问,我尝试版本ea-113和更新版本)开始,javac也接受此代码.
要了解编译器如何根据JLS对此进行分析,必须分清哪些是通配符捕获,类型变量,推理变量等.
类型c是Consumer<capture#1-of ?>(javac会写Consumer<CAP#1>).此类型未知,但已修复.
m2has类型的参数Consumer<? super T>,where T是要通过类型推断实例化的类型变量.
在类型推断期间,使用由ecj as表示的推理变量T#0来表示T.
类型推断在于确定是否T#0可以在不违反任何给定类型约束的情况下实例化为任何类型.在这个特殊情况下,我们从这个约束开始:
⟨c→消费者<?超级T#0>⟩
逐步减少(通过应用JLS 18.2):
⟨消费者<捕获#1 - >?>消费者<?超级T#0>⟩
⟨capture#1-of?<=?超级T#0⟩
⟨T#0 <:捕获#1-of?⟩
T#0 <:捕获#1?
最后一行是"类型绑定"并完成缩减.由于不涉及进一步的约束,因此解决方案可以简单地实例化T#0到该类型capture#1-of ?.
通过这些步骤,已经证明类型推断m2适用于此特定调用.QED.
直观地,所示解决方案告诉我们:无论捕获可以表示什么类型,如果T设置为表示完全相同的类型,则不违反类型约束.这是可能的,因为在开始类型推断之前捕获是固定的.
请注意,可以编译以下内容而不会出现问题:
public class Test {
public static final void m1(Consumer<?> c) {
m2(c);
}
private static final <T> void m2(Consumer<T> c) {
}
}
Run Code Online (Sandbox Code Playgroud)
尽管我们不知道消费者的实际类型,但我们知道它可以分配Consumer<T>,但我们不知道是什么T(不知道什么T是通用代码的规范).
但是如果赋值Consumer<T>是有效的,那么赋值Consumer<? super T>也是如此.我们甚至可以通过一个中间步骤来实现这一点:
public class Test {
public static final void m1(Consumer<?> c) {
m2(c);
}
private static final <T> void m2(Consumer<T> c) {
m3(c);
}
private static final <T> void m3(Consumer<? super T> c) {
}
}
Run Code Online (Sandbox Code Playgroud)
没有编译器对象.
当您使用命名类型替换通配符时,也会接受它,例如
public class Test {
public static final void m1(Consumer<?> c) {
m2(c);
}
private static final <E,T extends E> void m2(Consumer<E> c) {
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,E是一种超级类型T,就像? super T是.
我试图找到javac最接近这种情况的错误报告,但是当涉及到javac和通配符类型时,它们有很多,我最终放弃了.免责声明:这并不意味着有太多的错误,只是报告了很多相关的情况,这可能都是同一个错误的不同症状.
唯一重要的是,它已经在Java 9中得到修复.
| 归档时间: |
|
| 查看次数: |
318 次 |
| 最近记录: |