Java8 编译错误,Java6 没有错误,为什么?
Compiler error in Java 8, no error in Java 6, why?
我有这个代码:
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
class B { }
class C extends B { }
class D { }
class Test {
void test() {
List<B> lb = new ArrayList<B>();
List<C> lc = new ArrayList<C>();
Iterable<B> it = lb == null ? lc : lb; // a
FluentIterable.from(lb == null ? lc : lb).transform(new Function<B, D>() { // b
@Override
public D apply(B b) {
return null;
}
});
}
}
Under Java 8 行 //b 给我这些编译器错误:
Incompatible types. Found: 'java.util.List<C>', required: 'java.util.List<capture<? extends B>>'
Incompatible types. Found: 'java.util.List<B>', required: 'java.util.List<capture<? extends B>>'
在 Java 6 下,同一行编译正常。
行//a
Iterable<B> it = lb == null ? lc : lb;
产生编译错误
Incompatible types. Found: 'java.util.List<C>', required: 'java.lang.Iterable<B>'
在 Java 6 和 Java 8 下都是正确的。
但是 Guava 的 FluentIterable.from 只是 Iterable 的包装器。为什么它在 Java 6 下不产生任何错误而在 Java 8 下却产生错误?它与我在 //a?
行的内容有何不同
谢谢。
TL;DR 在 Java 8 中更改了类型推断的语言规范。
行 //a 是编译时错误,因为 List 不可分配给 Iterable 类型的变量。如果是,那么您可以编译以下不正确的代码
List<C> listOfC = new ArrayList<C>();
Iterable<B> listOfB = listOfC; // if this were allowed it would be bad
listOfB.add(new B()); // ...because now my list of C's has a B in it
C reallyAB = listOfC.get(0); // ...and now i have an object of type B in a variable of type C
reallyAB.methodThatsDefinedOnAC(); // ...and crash
编译器检测到不正确的赋值很简单,因为 //a 行的变量具有显式类型。
行 //b 更复杂,因为该方法具有泛型类型参数,因此编译器必须在检查参数的同时推断方法的类型.随着 Java 8 的发布,围绕如何推断类型参数的规则发生了重大变化,它将 Lambdas 引入了语言,旨在在大多数情况下改进类型推断。
https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html
此处详细分析了为什么三元运算符现在无法满足泛型类型约束:Generics compilation error with ternary operator in Java 8, but not in Java 7
规范之间的不兼容性记录在这个 jdk 错误中:https://bugs.openjdk.java.net/browse/JDK-8044053
您可以通过提供显式类型来克服规则中的限制。您可以将表达式结果分配给一个变量并将该变量传递给调用,或者您可以强制转换表达式。
void test() {
List<B> lb = new ArrayList<B>();
List<C> lc = new ArrayList<C>();
FluentIterable.from((List<? extends B>)(lb == null ? lc : lb)).transform(new Function<B, D>() { // b
@Override
public D apply(B b) {
return null;
}
});
}
我有这个代码:
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
class B { }
class C extends B { }
class D { }
class Test {
void test() {
List<B> lb = new ArrayList<B>();
List<C> lc = new ArrayList<C>();
Iterable<B> it = lb == null ? lc : lb; // a
FluentIterable.from(lb == null ? lc : lb).transform(new Function<B, D>() { // b
@Override
public D apply(B b) {
return null;
}
});
}
}
Under Java 8 行 //b 给我这些编译器错误:
Incompatible types. Found: 'java.util.List<C>', required: 'java.util.List<capture<? extends B>>'
Incompatible types. Found: 'java.util.List<B>', required: 'java.util.List<capture<? extends B>>'
在 Java 6 下,同一行编译正常。
行//a
Iterable<B> it = lb == null ? lc : lb;
产生编译错误
Incompatible types. Found: 'java.util.List<C>', required: 'java.lang.Iterable<B>'
在 Java 6 和 Java 8 下都是正确的。
但是 Guava 的 FluentIterable.from 只是 Iterable 的包装器。为什么它在 Java 6 下不产生任何错误而在 Java 8 下却产生错误?它与我在 //a?
行的内容有何不同谢谢。
TL;DR 在 Java 8 中更改了类型推断的语言规范。
行 //a 是编译时错误,因为 List
List<C> listOfC = new ArrayList<C>();
Iterable<B> listOfB = listOfC; // if this were allowed it would be bad
listOfB.add(new B()); // ...because now my list of C's has a B in it
C reallyAB = listOfC.get(0); // ...and now i have an object of type B in a variable of type C
reallyAB.methodThatsDefinedOnAC(); // ...and crash
编译器检测到不正确的赋值很简单,因为 //a 行的变量具有显式类型。
行 //b 更复杂,因为该方法具有泛型类型参数,因此编译器必须在检查参数的同时推断方法的类型.随着 Java 8 的发布,围绕如何推断类型参数的规则发生了重大变化,它将 Lambdas 引入了语言,旨在在大多数情况下改进类型推断。
https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html
此处详细分析了为什么三元运算符现在无法满足泛型类型约束:Generics compilation error with ternary operator in Java 8, but not in Java 7
规范之间的不兼容性记录在这个 jdk 错误中:https://bugs.openjdk.java.net/browse/JDK-8044053
您可以通过提供显式类型来克服规则中的限制。您可以将表达式结果分配给一个变量并将该变量传递给调用,或者您可以强制转换表达式。
void test() {
List<B> lb = new ArrayList<B>();
List<C> lc = new ArrayList<C>();
FluentIterable.from((List<? extends B>)(lb == null ? lc : lb)).transform(new Function<B, D>() { // b
@Override
public D apply(B b) {
return null;
}
});
}