首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java lambda返回lambda

Java lambda返回lambda
EN

Stack Overflow用户
提问于 2014-11-11 19:21:50
回答 4查看 12.6K关注 0票数 24

在新的JDK 8的函数式编程领域中,我试图做的似乎是一件相对基本的事情,但我无法让它开始工作。我有一个工作密码:

代码语言:javascript
复制
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());
   }
}

它获取一个数字列表,并生成一个可以打印出来的函数列表。但是,将显式强制转换为可调用似乎是多余的。对我和IntelliJ来说都是这样。我们都同意,这也应该有效:

代码语言:javascript
复制
List<Callable<Object>> checks = l.stream().
       map(n -> () -> {
          System.out.println(n);
          return null;
       }).
       collect(Collectors.toList());

然而,我得到了一个错误:

代码语言:javascript
复制
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
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-11-12 09:55:37

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

在这里,l.stream(). map(n -> () -> { System.out.println(n); return null; })collect(Collectors.toList())方法调用的接收方,因此不考虑目标类型List<Callable<Object>>

如果知道目标类型,那么很容易证明嵌套lambda表达式是有效的。

代码语言:javascript
复制
static <T> Function<T,Callable<Object>> toCallable() {
    return n -> () -> {
        System.out.println(n); 
        return null;
    };
}

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

代码语言:javascript
复制
List<Callable<Object>> checks = l.stream()
    .map(toCallable()).collect(Collectors.toList());

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

代码语言:javascript
复制
// 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);
}

并将原始表达式重写为

代码语言:javascript
复制
List<Callable<Object>> checks = collect(l.stream().map(
    n -> () -> {
        System.out.println(n); 
        return null;
    }), Collectors.toList());

这并不降低代码的复杂性,但可以编译而不存在任何问题。对我来说,这是一种似曾相识的感觉。当Java5和Generics出现时,程序员不得不在new表达式上重复类型参数,同时简单地将表达式包装到泛型方法中,这证明了推断类型是没有问题的。直到Java 7才允许程序员省略这些不必要的类型参数重复(使用“菱形运算符”)。现在,我们有了类似的情况,将调用表达式包装到另一种方法中,将接收方转换为参数,证明了这种限制是不必要的。所以也许我们可以在Java 10…中消除这个限制。

票数 24
EN

Stack Overflow用户

发布于 2017-10-24 19:33:27

我遇到了同样的问题,并能够通过向map显式指定泛型类型参数来解决这个问题,如下所示:

代码语言:javascript
复制
List<Callable<Object>> checks = l.stream().
   <Callable<Object>>map(n -> () -> {
      System.out.println(n); 
      return null;
   }).
   collect(Collectors.toList());
票数 6
EN

Stack Overflow用户

发布于 2014-11-11 20:05:03

我还没有深入研究类型推断如何与lambdas一起工作的确切规则。然而,从一般语言设计的角度来看,编写语言规则并不总是可能的,这样编译器就可以理解我们认为它应该做的一切。我曾经是Ada语言编译器的编译器维护者,我熟悉那里的许多语言设计问题。Ada在很多情况下使用类型推断(如果不查看包含构造的整个表达式,就无法确定构造的类型,我认为这个Java lambda表达式也是如此)。在理论上只有一种可能的解释时,有一些语言规则会导致编译器拒绝某些含糊的表达式。如果我没记错的话,原因之一是有人发现了这样一种情况,一条让编译器计算出正确解释的规则会要求编译器进行17遍表达式,才能正确解释它。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26872827

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档