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;
            }
        });
    }