java - 代码在 Eclipse 中编译,但在 javac 中编译:带有功能子接口的 curried lambdas。哪个是对的?
问题描述
我在 Eclipse 中开发了一些代码,测试成功,将其推送到我们的 Jenkins CI 服务器,并收到一封电子邮件,称 Maven 因 Java 编译错误而窒息。我随后隔离了该问题并创建了以下显示该问题的最小示例:
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()
将from的参数类型更改MyFunction
为Function
,或删除参数类型中的通配符? extends
,会使示例代码在 javac 中编译。
显然,Eclipse 或 javac 都违反了 Java 语言规范。问题是,我是在 Eclipse 还是 javac 上提交错误报告?泛型 lambda 的类型推断规则非常复杂,以至于根据 JLS,我不知道这个程序是否是合法的 Java。
动机说明
在原始代码中,
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) 版本号:20180917-1800
- 2019-03 RC1 (4.11 RC1) 版本号:20190307-2044
测试的 javac 版本:
- 1.8.0_121
- JDK 10.0.1 通过JDoodle 在线 Java 编译器
解决方案
It looks like you run into JDK bug JDK-8156954 which has been fixed in Java 9 but not in Java 8.
It is a bug of Java 8 javac
because in your example all variable types of the transform
method can be inferred without violating the Java language specification as follows:
F
:String
(via first parameteroriginalList
of typeList<String>
)T
:IntToByteFunction
(via return typeList<IntToByteFunction>
)
These inferred variable types are compatible with the type of the second parameter, the chained lambda expression:
outer -> inner -> doStuff(inner, outer)
resolves (withdoStuff(Integer, String)
toString -> Integer -> doStuff(Integer, String)
resolves toString -> Integer -> Byte
is compatible withString -> IntToByteFunction
is compatible withMyFunction<? super String, ? extends IntToByteFunction>
Your example can be minimized further:
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
overrides the same with the same (R apply(T t);
). If Function
instead of MyFunction
is used or if MyFunction
extends Function
but without @Override R apply(T t);
then the error disappears. Also with F
instead of ? extends F
the error disappears.
Even if your example differs from the example in the mentioned bug, it can be assumed that it is the same bug because it is the only "argument mismatch; bad return type in lambda expression bug that has been fixed in Java 9 but not in Java 8 and that occurs only with lambda functions in combination with Java Generics.
推荐阅读
- c# - C# 无法访问类中的属性
- session - 来自 Spring 的 Cookie 未保存在浏览器中
- firebase - 集成 FireStore/Stripe/iOS/Cloud 功能
- ios - 插入带有可编辑 UITextfields 的新 UITableView 行
- c# - 使用 DESCryptographer 解密时,加密文本返回奇怪的字符
- python - 无法绘制饼图
- react-native - 反应本机动作按钮滚动隐藏显示自下而上动画
- python - 替换数据框的多个特定列中的值
- python - 将字符串更改为五个 python 的块
- android - Abi 过滤器 - 被一个或多个具有更高版本代码的 APK 遮蔽