Java 8 中的编译器错误,Java 6 中没有错误,为什么?

问题描述

我有这个代码

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

在 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 的发布,关于如何推断类型参数的规则发生了显着变化,Java 8 将 Lambda 引入到语言中,旨在改进大多数情况下的类型推断。

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 bug中: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;
            }
        });
    }

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...