Java lambda 返回 lambda

问题描述 投票:0回答:5

我正在尝试做一些在新的 JDK 8 函数式编程领域中似乎相对基本的事情,但我无法让它工作。我有这个工作代码:

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

public class so1 {
   public static void main() {
      List<Number> l = new ArrayList<>(Arrays.asList(1, 2, 3));
      List<Callable<Object>> checks = l.stream().
               map(n -> (Callable<Object>) () -> {
                  System.out.println(n);
                  return null;
               }).
               collect(Collectors.toList());
   }
}

它接受一个数字列表并生成一个可以打印它们的函数列表。然而,显式转换为 Callable 似乎是多余的。在我和 IntelliJ 看来。我们都同意这也应该有效:

List<Callable<Object>> checks = l.stream().
       map(n -> () -> {
          System.out.println(n);
          return null;
       }).
       collect(Collectors.toList());

但是我收到错误:

so1.java:10: error: incompatible types: cannot infer type-variable(s) R
      List<Callable<Object>> checks = l.stream().map(n -> () -> {System.out.println(n); return null;}).collect(Collectors.toList());
                                                    ^
    (argument mismatch; bad return type in lambda expression
      Object is not a functional interface)
  where R,T are type-variables:
    R extends Object declared in method <R>map(Function<? super T,? extends R>)
    T extends Object declared in interface Stream
1 error
java lambda functional-programming java-8
5个回答
25
投票

您遇到了 Java 8 目标类型的限制,该限制适用于方法调用的“接收器”。虽然目标类型适用于参数类型(大多数情况下),但它不适用于您调用方法的对象或表达式。 这里,

l.stream(). map(n -> () -> { System.out.println(n); return null; })

collect(Collectors.toList())
方法调用的接收者,因此不考虑目标类型
List<Callable<Object>>

如果目标类型已知,则很容易证明嵌套 lambda 表达式有效,例如

static <T> Function<T,Callable<Object>> toCallable() { return n -> () -> { System.out.println(n); return null; }; }

工作没有问题,您可以使用它来解决您原来的问题

List<Callable<Object>> checks = l.stream() .map(toCallable()).collect(Collectors.toList());

您还可以通过引入辅助方法来解决问题,该方法将第一个表达式的角色从方法接收者更改为参数

// turns the Stream s from receiver to a parameter static <T, R, A> R collect(Stream<T> s, Collector<? super T, A, R> collector) { return s.collect(collector); }

并将原始表达式重写为

List<Callable<Object>> checks = collect(l.stream().map( n -> () -> { System.out.println(n); return null; }), Collectors.toList());

这不会降低代码的复杂性,但可以毫无问题地编译。对我来说,这是一种似曾相识的感觉。当 Java 5 和泛型出现时,程序员必须在 
new

表达式上重复类型参数,同时简单地将表达式包装到泛型方法中,证明推断类型没有问题。直到 Java 7 才允许程序员省略这些不必要的类型参数重复(使用“菱形运算符”)。现在我们有类似的情况,将调用表达式包装到另一个方法中,将接收者变成参数,证明这种限制是不必要的。所以也许我们可以在 Java 10 中摆脱这个限制……

    


7
投票
map

如下所示:


List<Callable<Object>> checks = l.stream(). <Callable<Object>>map(n -> () -> { System.out.println(n); return null; }). collect(Collectors.toList());



2
投票

因此,虽然我们可能认为编译器“应该”能够在特定情况下解决某些问题,但这可能根本不可行。


2
投票

Function<Integer, Callable<Object>> fn = n -> () -> { System.out.println(n); return null; }

这就是 lambda 获取其类型的方式:
Function<Integer, Callable<Object>>


那么就得看看泛型类型中的类型推断: map 的返回类型是

<R> Stream<R>

,R 将由您传递给函数的参数类型决定。如果您

map(x->"some string")
,那么结果就是
Stream<String>
。现在这就是问题所在,R 的类型是 lambda 的类型。但 lambda 需要一个目标类型,即变量 R。

工作代码之所以有效,是因为它显式地将 lambda 转换为类型。


0
投票

Target type of a lambda conversion must be an interface

Java 不知道您在编译时尝试返回的 lambda 类型,因此添加通用类型参数应该会有所帮助:

List<Callable<Object>> checks = l.stream(). map(n -> (Callable<Object>) () -> { System.out.println(n); return null; }). collect(Collectors.toList());

List<Callable<Object>> checks = l.stream(). <Callable<Object>>map(n -> () -> { System.out.println(n); return null; }). collect(Collectors.toList());

以上任一方法都可以解决问题。

© www.soinside.com 2019 - 2024. All rights reserved.