【发布时间】:2018-06-21 07:34:00
【问题描述】:
假设我们有一个这样的 lambda 函数:
Function<ArrayList<Integer>, int[]> func1 = a->new int[2];
它的作用并不重要。重要的是:输入是ArrayList<Integer>,输出是int[]。
使用一些基本的测试编译和运行没有问题:
int[] func1Result1 = func1.apply(new ArrayList<Integer>()); // Non-currying works with <Integer>
System.out.println(func1Result1);
System.out.println(func1Result1.getClass());
System.out.println(Arrays.toString(func1Result1));
System.out.println();
int[] func1Result2 = func1.apply(new ArrayList<>()); // Non-currying works with <>
System.out.println(func1Result2);
System.out.println(func1Result2.getClass());
System.out.println(Arrays.toString(func1Result2));
System.out.println();
int[] func1Result3 = func1.apply(new ArrayList()); // Non-currying works without <>
System.out.println(func1Result3);
System.out.println(func1Result3.getClass());
System.out.println(Arrays.toString(func1Result3));
System.out.println();
现在假设我们有一个像这样的柯里化 lambda 函数:
Function<Object, Function<Object, int[]>> func2 = a->b->new int[2];
同样,它的作用并不重要。这次柯里化函数接受了两个Object参数,仍然输出一个int[]。
使用相同的基本测试编译并再次运行没有问题:
int[] func2Result1 = func2.apply(new ArrayList<Integer>()).apply(null); // Currying works with <Integer>
System.out.println(func2Result1);
System.out.println(func2Result1.getClass());
System.out.println(Arrays.toString(func2Result1));
System.out.println();
int[] func2Result2 = func2.apply(new ArrayList<>()).apply(null); // Currying works with <>
System.out.println(func2Result2);
System.out.println(func2Result2.getClass());
System.out.println(Arrays.toString(func2Result2));
System.out.println();
int[] func2Result3 = func2.apply(new ArrayList()).apply(null); // Currying works without <>
System.out.println(func2Result3);
System.out.println(func2Result3.getClass());
System.out.println(Arrays.toString(func2Result3));
System.out.println();
现在是第三个变体,这是我的困惑所在和我的问题所在。假设我们有一个像这样的柯里化 lambda 函数:
Function<ArrayList<Integer>, Function<Object, int[]>> func3 = a->b->new int[2];
这次参数是ArrayList<Integer>和Object,返回类型还是int[]。
这次使用同样的基本测试编译不通过,报错:
int[] func3Result1 = func3.apply(new ArrayList<Integer>()).apply(null); // Currying works with <Integer>
System.out.println(func3Result1);
System.out.println(func3Result1.getClass());
System.out.println(Arrays.toString(func3Result1));
System.out.println();
int[] func3Result2 = func3.apply(new ArrayList<>()).apply(null); // Currying works with <>
System.out.println(func3Result2);
System.out.println(func3Result2.getClass());
System.out.println(Arrays.toString(func3Result2));
System.out.println();
int[] func3Result3 = func3.apply(new ArrayList()).apply(null); // Currying doesn't work without <>
System.out.println(func3Result3);
System.out.println(func3Result3.getClass());
System.out.println(Arrays.toString(func3Result3));
System.out.println();
错误是:
Main.java:23:错误:不兼容的类型:对象无法转换为 int[]
int[] func3Result3 = func3.apply(new ArrayList()).apply(null); // 如果没有
,Currying 不起作用 ^
为什么它认为返回类型是Object 而不是int[]?内部函数的返回类型清楚地表明返回类型是int[]。如果两个 lambda 的参数都是 Object(func2 测试),或者集合附有菱形(func3Result1 和 func3Result2),它确实可以正常工作。但是由于某种原因,当从集合中移除钻石时 (func3Result3) 会感到困惑,即使 int[] 返回类型与此 ArrayList<Integer> 输入无关。
编辑:刚刚在我的 jdk 版本 1.8.0_72 上对其进行了本地测试,它确实可以编译和工作。有人可以确认它确实不适用于 jdk 1.9 或 1.10(或最新版本的 jdk 1.8 之一)吗?也许问题是 TIO 在这里做了一些奇怪的事情而不是 JDK 本身.. :S
【问题讨论】:
-
@Holger 感谢您的阅读。我确实知道你永远不应该使用它,尽管现在我也更好地理解了它背后的原因。我仍然有点困惑Java如何以某种方式将
ArrayList输入类型与currying lambda中的int[]返回类型联系起来,即使它们彼此无关。我很好奇是否有人可以在这些特定情况下在这些 currying lambdas 中提供编译器中发生的实际 JVM 代码。 -
在 Java 8 之前,您可以简单地说,使用原始类型会导致表达式根本不使用泛型,因此即使从完全不相关的类型变量中也可能无法推断类型。众所周知的例子,
List list; … String[] array = list.toArray(new String[0]);由于使用了原始类型List而不起作用,即使toArray的类型参数与List的类型参数无关。从 Java 8 及其扩展的目标类型推断开始,规则变得更加复杂,看到编译器之间的差异(错误、修复的错误、规范清理)也就不足为奇了。 -
从 Java9 及以上,他们已经严格了泛型参数的推断,这可能是为什么
标签: lambda types java-8 currying java-10