java lambda返回一个lambda
我正在尝试在function性编程的新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()); } }
它需要一个数字列表,并产生一个可以打印出来的函数列表。 然而,对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 8的目标types的限制。 尽pipe目标键入对于参数types(大多数情况下)是有效的,但它不适用于调用方法的对象或expression式。
在这里, l.stream(). map(n -> () -> { System.out.println(n); return null; })
l.stream(). map(n -> () -> { System.out.println(n); return null; })
是collect(Collectors.toList())
方法调用的接收者,所以目标typesList<Callable<Object>>
不考虑它。
很容易certificate,如果目标types是已知的,嵌套的lambdaexpression式就可以工作
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());
您也可以通过引入一个辅助方法来解决问题,该方法将第一个expression式的作用从方法接收方改为参数
// 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); }
并重写原始expression式为
List<Callable<Object>> checks = collect(l.stream().map( n -> () -> { System.out.println(n); return null; }), Collectors.toList());
这不会降低代码的复杂性,但可以毫无问题地进行编译。 对我来说,这是一个似曾相识。 当Java 5和generics出来时,程序员不得不在new
expression式上重复types参数,而只是将expression式包装成一个通用的方法,certificate推断types是没有问题的。 直到Java 7才允许程序员省略这些不必要的types参数重复(使用“钻石算子”)。 现在我们有类似的情况,将调用expression式包装到另一个方法中,将接收器转换为参数,certificate这个限制是不必要的。 所以也许我们在Java 10中摆脱了这个限制…
我还没有深入研究types推理如何与lambdas一起工作的确切规则。 然而,从一般的语言deviseangular度来看,编写语言规则并不总是可能的,编译器能够找出我们认为应该做的所有事情。 我一直是Ada语言编译器的编译器维护者,我熟悉那里的许多语言devise问题。 Ada在很多情况下使用types推断(如果没有查看包含构造的整个expression式,我认为这种Java lambdaexpression式也是这种情况,构造的types不能被确定)。 有一些语言规则会导致编译器拒绝一些含糊的expression,理论上只有一种可能的解释。 其中一个原因是,如果我没有记错的话,是有人发现了一个可以让编译器找出正确解释的规则的话,那么编译器就需要通过一个expression式来传递17次,才能正确解释它。
所以,虽然我们可能认为编译器“应该”能够在某个特定的情况下弄清楚什么,但是这可能是不可能的。
我遇到了同样的问题,并能够通过显式指定genericstypes参数来map
如下所示:
List<Callable<Object>> checks = l.stream(). <Callable<Object>>map(n -> () -> { System.out.println(n); return null; }). collect(Collectors.toList());