java lambda返回一个lambda [英] java lambda returning a lambda

查看:107
本文介绍了java lambda返回一个lambda的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在功能性编程的新jdk8领域中做一些似乎相对基本的事情,但无法实现它。我有这个工作代码:

  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());




$ b $ p
$ b

它包含一个数字列表并产生一个列表可以打印出来的功能。然而,对Callable的显式转换似乎是多余的。在我看来,对于IntelliJ来说。我们都同意这也应该起作用:

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

然而我得到一个错误:

  so1.java:10:错误:不兼容的类型:无法推断类型变量R 
List< Callable< Object>> (Collector.toList()); checks = l.stream()。map(n - >() - > {System.out.println(n); return null;})。
$
(参数不匹配; lambda表达式中的返回类型不正确
Object不是函数接口)
其中R,T是类型变量:
R extends Object在方法< R> map中声明(函数<超级T,?扩展R>)
T扩展接口中声明的对象流
1错误
pre>

解决方案

你遇到了适用于接收器的Java 8的目标类型限制调用。尽管目标类型对参数类型起作用(大部分时间),但它不适用于您调用方法的对象或表达式。



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

很容易证明嵌套的lambda表达式在目标类型已知的情况下工作,例如

  static< T>功能< T,可赎回<对象>> toCallable(){
return n - > () - > {
System.out.println(n);
返回null;
};
}

没有任何问题,您可以用它来解决原始问题, p>

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

您也可以通过引入一个辅助方法来解决问题,该方法从方法中改变第一个表达式的角色接收器转换为参数

  //将Stream从接收器变为参数
static< T,R, A> R收集(Stream T,收集器<?super T,A,R>收集器){
return s.collect(收集器);
}

并将原始表达式重写为

  List< Callable< Object>> ($)bn  - >() - > {
System.out.println(n);
返回null;
} ),Collectors.toList());

这不会降低代码的复杂性,但可以毫无问题地进行编译。对我来说,这是一个似曾相识。当Java 5和泛型出来时,程序员必须在 new 表达式上重复类型参数,而只需将表达式包装为泛型方法,证明推断类型不成问题。在Java 7之前,程序员被允许省略这些不必要的类型参数重复(使用钻石算子)。现在我们有类似的情况,将调用表达式包装到另一个方法中,将接收器转换为参数,证明这种限制是不必要的。所以,也许我们在Java 10中摆脱了这个限制...

I am trying to do what seems to be a relatively basic thing in the new jdk8 land of functional programming but can't get it work. I have this working code:

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());
   }
}

It takes a list of numbers and produces a list of functions that can print them out. However the explicit cast to Callable seems redundant. It seems to to me and to IntelliJ. And we both agree that this should also work:

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

However I get an error:

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

解决方案

You hit a limitation of Java 8’s target typing which applies to the receiver of a method invocation. While target typing works (most of the times) for parameter types it does not work for the object or expression on which you invoke the method.

Here, l.stream(). map(n -> () -> { System.out.println(n); return null; }) is the receiver of the collect(Collectors.toList()) method invocation, so the target type List<Callable<Object>> is not considered for it.

It’s easy to prove that nested lambda expressions work if the target type is know, e.g.

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

works without problems and you can use it to solve your original problem as

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

You can also solve the problem by introducing a helper method which changes the role of the first expression from method receiver to a parameter

// 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);
}

and rewrite the original expression as

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

This does not reduce the complexity of the code but can be compiled without any problems. For me, it’s a déjà vu. When Java 5 and Generics came out, programmers had to repeat the type parameters on new expressions while simply wrapping the expression into a generic method proved that inferring the type is no problem. It took until Java 7 before programmers were allowed to omit these unnecessary repetition of the type arguments (using the "diamond operator"). Now we have a similar situation, wrapping an invocation expression into another method, turning the receiver into a parameter, proves that this limitation is unnecessary. So maybe we get rid of this limitation in Java 10…

这篇关于java lambda返回一个lambda的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆