Java 8 currying函数,无法确定int []返回类型

Kev*_*sen 4 lambda types currying java-8 java-10

假设我们有一个像这样的lambda函数:

Function<ArrayList<Integer>, int[]> func1 = a->new int[2];
Run Code Online (Sandbox Code Playgroud)

它的作用并不重要.重要的是:输入是一个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();
Run Code Online (Sandbox Code Playgroud)

在线尝试.


现在让我们假设我们有一个像这样的currying lambda函数:

Function<Object, Function<Object, int[]>> func2 = a->b->new int[2];
Run Code Online (Sandbox Code Playgroud)

同样,它的作用并不重要.这次currying函数有两个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();
Run Code Online (Sandbox Code Playgroud)

在线尝试.


现在是第三个变种,这是我的困惑,我的问题在哪里.假设我们有一个像这样的currying lambda函数:

Function<ArrayList<Integer>, Function<Object, int[]>> func3 = a->b->new int[2];
Run Code Online (Sandbox Code Playgroud)

这次参数是a ArrayList<Integer>和an 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();
Run Code Online (Sandbox Code Playgroud)

错误是:

Main.java:23:错误:不兼容的类型:对象无法转换为int []
 int [] func3Result3 = func3.apply(new ArrayList()).apply(null); //没有<>
                                                                                       ^,Currying不起作用

在线尝试.

为什么它认为返回类型是一个Object而不是int[]?内部函数的返回类型清楚地表明返回类型int[].如果两个lambda的参数都是Object(func2测试),或者Collection附有钻石(func3Result1func3Result2),它确实可以正常工作.但是由于某种原因,当钻石从Collection(func3Result3)中移除时它会变得混乱,即使int[]返回类型与此ArrayList<Integer>输入无关.

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

Lin*_*ica 6

简而言之.当您使用原始类型时(不应在评论中使用@Holger中提到的那些),您将删除任何通用信息.所以行:

int[] func3Result3 = func3.apply(new ArrayList()).apply(null);
Run Code Online (Sandbox Code Playgroud)

可以分为多行以便澄清:

Function temp = func3.apply(new ArrayList());
Run Code Online (Sandbox Code Playgroud)

这里仅Function返回,因为通过使用原始类型擦除了通用信息new ArrayList().

原始类型函数有点相似,但不等于 Function<Object, Object>

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

int[] func3Result3 = temp.apply(null);
Run Code Online (Sandbox Code Playgroud)

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

所以这个道德是:

切勿使用原始类型.它们只是一个向后兼容的功能,永远不应该在现代生产代码中使用.

  • @KevinCruijssen这正是发生的事情.一旦使用原始类型.使用该原始类型的所有其他内容也将丢失类型信息. (4认同)
  • 这可以在包含其他泛型类型的对象的泛型类中最好地看到.例如`class Box <T> {List <String>字符串; List <String> getStrings(){return strings}}`如果你没有为`Box`指定泛型类型参数,那么`getStrings()`将只返回一个`List`,尽管看起来似乎没有受到影响.它仍然是 (3认同)