Java 8 currying 函数,无法确定 int[] return-type

Java 8 currying Function, unable to determine int[] return-type

假设我们有一个这样的 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();

Try it online.


现在假设我们有一个像这样的柯里化 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();

Try it online.


现在是第三个变体,这是我的困惑所在,也是我的问题所在。假设我们有一个像这样的柯里化 lambda 函数:

Function<ArrayList<Integer>, Function<Object, int[]>> func3 = a->b->new int[2];

这次的参数是一个ArrayList<Integer>和一个Object,return-type还是一个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: error: incompatible types: Object cannot be converted to int[]
 int[] func3Result3 = func3.apply(new ArrayList()).apply(null); // Currying doesn't work without <>
                                                                                       ^

Try it online.

为什么它认为 return-type 是 Object 而不是 int[]?内部函数的 return-type 明确指出 return-type 是 int[]。如果两个 lambda 的参数都是 Objectfunc2 测试),或者 Collection 附有菱形(func3Result1func3Result2),它确实可以正常工作.但是出于某种原因,当钻石从 Collection (func3Result3) 中移除时,它会感到困惑,即使 int[] return-type 与此无关 ArrayList<Integer> 输入。

编辑:刚刚在我的 jdk 版本 1.8.0_72 上对其进行了本地测试,它确实可以编译和运行。有人可以确认它确实不适用于 jdk 1.9 或 1.10(或 jdk 1.8 的最新版本之一)吗?也许问题是 TIO 在这里做了一些奇怪的事情而不是 JDK 本身.. :S

简而言之。当您使用原始类型(@Holger 在评论中提到的you should not use them)时,您会删除任何通用信息。所以行:

int[] func3Result3 = func3.apply(new ArrayList()).apply(null);

可以分成多行进行说明:

Function temp = func3.apply(new ArrayList());

这里只返回Function,因为通用信息被原始类型new ArrayList()的使用擦除。

和一个原始类型的函数有些相似,但不等于Function<Object, Object>

这使得现在很容易看出,将 null 应用于该函数时,您并不完全知道返回的内容(除了它是一个 Object),那就是为什么会出现该错误:

int[] func3Result3 = temp.apply(null);

由于类型擦除,编译器只是不知道类型。

所以这句话的寓意是:

Never use raw types. They are just a backward compatibility feature which should never be used in modern production code.