【问题标题】:Compiler error in Java 8, no error in Java 6, why?Java 8 中的编译器错误,Java 6 中没有错误,为什么?
【发布时间】:2021-06-14 22:01:41
【问题描述】:

我有这个代码:

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 行的内容有何不同?

谢谢。

【问题讨论】:

  • Line a 应该是 Iterable&lt;? extends B&gt; it = lb == null ? lc : lb; 然后它将等同于 Java 6 为 line b 推断的内容。
  • @Holger 谢谢,我不太明白为什么 Java 8 会出错,而 Java 6 不会在 b 行。你能给我点提示吗?
  • 我也不明白。规则非常复杂,并且由于目标类型的合并而发生了变化。但由于此时没有可用的目标类型,我希望它的行为类似于 Java 6。

标签: java-8 compiler-errors guava iterable java-6


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-23
    • 2011-03-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多