代码在Eclipse中编译,而不是Javac:带有功能子界面的咖喱兰伯.哪个是对的



我在Eclipse中开发了一些代码,成功测试了它,将其推向了我们的Jenkins CI服务器,并收到了一封电子邮件,Maven正在用Java编译错误cho住。随后,我隔离了这个问题,并创建了以下最小示例,显示了问题:

import java.util.List;
import java.util.function.Function;
class MinimalTypeFailureExample {
    public static void main(String[] args) {
        List<String> originalList = null;  // irrelevant
        List<IntToByteFunction> resultList = transform(originalList,
                outer -> inner -> doStuff(inner, outer));
        System.out.println(resultList);
    }
    static <F, T> List<T> transform(List<F> originalList,
            MyFunction<? super F, ? extends T> function) {
        return null;  // irrelevant
    }
    static Byte doStuff(Integer inner, String outer) {
        return null;  // irrelevant
    }
}
@FunctionalInterface
interface MyFunction<F, T> extends Function<F, T> {
    @Override
    T apply(F input);
}
@FunctionalInterface
interface IntToByteFunction {
    Byte applyIntToByte(Integer inner);
}

在Eclipse中,此代码无错误地编译,并且似乎按预期执行。但是,使用Javac编译给出以下错误:

MinimalTypeFailureExample.java:7: error: incompatible types: cannot infer type-variable(s) F,T
                List<IntToByteFunction> resultList = transform(originalList, outer -> inner -> doStuff(inner, outer));
                                                              ^
    (argument mismatch; bad return type in lambda expression
      T is not a functional interface)
  where F,T are type-variables:
    F extends Object declared in method <F,T>transform(List<F>,MyFunction<F,? extends T>)
    T extends Object declared in method <F,T>transform(List<F>,MyFunction<F,? extends T>)
1 error

transform()的参数类型从MyFunction更改为Function,或在参数类型中删除通配符? extends,使示例代码在Javac中编译。

显然,Eclipse或Javac都违反了Java语言规范。问题是,我是否在Eclipse或Javac 上提交错误报告?通用lambdas的类型推理规则是如此复杂,以至于我不知道该程序是否是根据JLS进行的。

动机注意

在原始代码中,transform()是Guava的com.google.common.collect.Lists.transform()MyFunction接口是Guava的com.google.common.base.Function接口,它由于历史原因而扩展java.util.function.Function

此代码的目的是创建第一个类型列表的视图,作为第二种类型的列表。第二种类型是一种功能接口类型,我想根据输入列表中的值构建的该类型的功能填充输出列表,因此,咖喱lambda表达式。

可重复性的版本信息

Eclipse版本测试:

  • 2018-09(4.9.0)构建ID:20180917-1800
  • 2019-03 RC1(4.11 RC1)构建ID:20190307-2044

测试的Javac版本:

  • 1.8.0_121
  • JDK 10.0.1通过Jdoodle在线Java编译器

看起来您遇到 JDK BUG JDK-8156954 已固定在Java 9中,但不在Java 8中。

这是Java 8 javac的错误,因为在您的示例中,可以推断出transform方法的所有变量类型 可以在不违反Java语言规范的情况下推断出:

  • FString (通过类型List<String>的第一个参数originalList
  • TIntToByteFunction (通过返回类型List<IntToByteFunction>

这些推断的变量类型兼容第二个参数的类型,链接的lambda 表达式:

  • outer -> inner -> doStuff(inner, outer)解决方案(使用doStuff(Integer, String)
  • String -> Integer -> doStuff(Integer, String)解决
  • String -> Integer -> Byte
  • 兼容
  • String -> IntToByteFunction
  • 兼容
  • MyFunction<? super String, ? extends IntToByteFunction>

您的示例可以进一步最小化:

import java.util.function.Function;
class MinimalTypeFailureExample {
    void foo() {
        transform((Function<Integer, String>)null, o -> i -> {return "";});
    }
    <T, F> void transform(F f, MyFunction<T, ? extends F> m) {}
}
@FunctionalInterface
interface MyFunction<T, R> extends Function<T, R> {
    @Override
    R apply(T t);
}

MyFunction用相同的(R apply(T t);)覆盖相同。如果使用Function而不是MyFunction,或者MyFunction扩展了Function,但没有@Override R apply(T t);,则错误会消失。同样,使用F而不是? extends F,错误消失了。

即使您的示例与上述错误中的示例有所不同,也可以假定它是同一错误,因为它是唯一的"参数不匹配; lambda expression in lambda expression bug中的错误返回类型已固定在Java 9中,但不在Java 8中固定,仅与Java Generics结合使用Lambda功能。

我尝试使用Javac 11.0.2尝试了示例代码,但没有收到任何错误。这表明该错误可能是在Javac中,并且在最近的版本中已固定。我对此有些惊讶,因为如前所述,我确实尝试了在线界面中测试JDK 10。

我对其他答案开放,这些答案提供了有关特定问题的更多详细信息,例如该问题的JDK错误号。

作为使代码编译在JDK 8中的解决方法,可以将显式铸件添加到Inner Lambda表达式:

List<IntToByteFunction> resultList = transform(originalList,
        outer -> (IntToByteFunction) inner -> doStuff(inner, outer));

最新更新