Luk*_*der 21 java type-inference compiler-errors java-8
以下程序在Java 7和Eclipse Mars RC2 for Java 8中编译:
import java.util.List;
public class Test {
static final void a(Class<? extends List<?>> type) {
b(newList(type));
}
static final <T> List<T> b(List<T> list) {
return list;
}
static final <L extends List<?>> L newList(Class<L> type) {
try {
return type.newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用javac 1.8.0_45编译器,报告以下编译错误:
Test.java:6: error: method b in class Test cannot be applied to given types;
b(newList(type));
^
required: List<T>
found: CAP#1
reason: inference variable L has incompatible bounds
equality constraints: CAP#2
upper bounds: List<CAP#3>,List<?>
where T,L are type-variables:
T extends Object declared in method <T>b(List<T>)
L extends List<?> declared in method <L>newList(Class<L>)
where CAP#1,CAP#2,CAP#3 are fresh type-variables:
CAP#1 extends List<?> from capture of ? extends List<?>
CAP#2 extends List<?> from capture of ? extends List<?>
CAP#3 extends Object from capture of ?
Run Code Online (Sandbox Code Playgroud)
解决方法是在本地分配变量:
import java.util.List;
public class Test {
static final void a(Class<? extends List<?>> type) {
// Workaround here
List<?> variable = newList(type);
b(variable);
}
static final <T> List<T> b(List<T> list) {
return list;
}
static final <L extends List<?>> L newList(Class<L> type) {
try {
return type.newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道Java 8中的类型推断已经发生了很大变化(例如,由于JEP 101"广义目标类型推断").那么,这是一个错误还是一种新的语言"功能"?
编辑:我还向Oracle报告了这个JI-9021550,但是为了防止这是Java 8中的"功能",我也向Eclipse报告了这个问题:
免责声明 - 我对这个主题知之甚少,以下是我试图证明javac行为的非正式推理.
我们可以将问题减少到
<X extends List<?>> void a(Class<X> type) throws Exception
{
X instance = type.newInstance();
b(instance); // error
}
<T> List<T> b(List<T> list) { ... }
Run Code Online (Sandbox Code Playgroud)
为了推断T,我们有约束
X <: List<?>
X <: List<T>
Run Code Online (Sandbox Code Playgroud)
从本质上讲,这是无法解决的.例如,T如果没有则存在X=List<?>.
不确定Java7如何推断这种情况.但是,javac8(和IntelliJ)表现得"合理",我会说.
现在,为什么这个解决方法有效?
List<?> instance = type.newInstance();
b(instance); // ok!
Run Code Online (Sandbox Code Playgroud)
它的工作原理是通配符捕获,它引入了更多类型信息,"缩小"类型 instance
instance is List<?> => exist W, where instance is List<W> => T=W
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不是在什么时候完成instance的X,因此可以使用较少的类型信息.
可以想象,语言也可以"改进"以对X进行通配符捕获:
instance is X, X is List<?> => exist W, where instance is List<W>
Run Code Online (Sandbox Code Playgroud)
感谢错误报告,感谢Holger,您的答案中的示例.这些和其他几个人最后让我质疑11年前在Eclipse编译器中做出的一个小改动.关键是:Eclipse非法扩展了捕获算法,以递归方式应用于通配符边界.
有一个例子,这个非法的变化完全符合Eclipse与javac的行为.与JLS中我们能够清楚地看到的一样,Eclipse开发人员已经相信这一旧决定.今天我认为以前的偏差必然有不同的原因.
今天我鼓励在这方面将ecj与JLS对齐,并且看起来很难破解的5个错误,基本上已经解决了这个问题(加上一点点调整以及补偿).
Ergo:是的,Eclipse有一个bug,但是这个bug已经修复了4.7里程碑2 :)
以下是ecj今后将报告的内容:
The method b(List<T>) in the type Test is not applicable for the arguments (capture#1-of ? extends List<?>)
Run Code Online (Sandbox Code Playgroud)
它是捕获范围内的通配符,找不到检测兼容性的规则.更确切地说,在推理期间的某些时间(精确地并入)我们遇到以下约束(T#0表示推理变量):
?T#0 = ??
Run Code Online (Sandbox Code Playgroud)
天真地,我们可以将类型变量解析为通配符,但是 - 可能因为通配符不被视为类型 - 减少规则将上面定义为减少为FALSE,从而使推理失败.
感谢bayou.io的回答,我们可以将问题缩小到这个事实
<X extends List<?>> void a(X instance) {
b(instance); // error
}
static final <T> List<T> b(List<T> list) {
return list;
}
Run Code Online (Sandbox Code Playgroud)
产生错误的同时
<X extends List<?>> void a(X instance) {
List<?> instance2=instance;
b(instance2);
}
static final <T> List<T> b(List<T> list) {
return list;
}
Run Code Online (Sandbox Code Playgroud)
可以编译没有问题.赋值instance2=instance是一个扩展转换,也应该发生在方法调用参数中.因此,这个答案的模式的差异是额外的子类型关系.
请注意,虽然我不确定这个特定情况是否与Java语言规范一致,但是一些测试表明Eclipse接受代码可能是由于它对于通用类型通常更为草率,如下所示,绝对不正确,代码可以编译没有任何错误或警告:
public static void main(String... arg) {
List<Integer> l1=Arrays.asList(0, 1, 2);
List<String> l2=Arrays.asList("0", "1", "2");
a(Arrays.asList(l1, l2));
}
static final void a(List<? extends List<?>> type) {
test(type);
}
static final <Y,L extends List<Y>> void test(List<L> type) {
L l1=type.get(0), l2=type.get(1);
l2.set(0, l1.get(0));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15459 次 |
| 最近记录: |