在新的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());
}
}它获取一个数字列表,并生成一个可以打印出来的函数列表。但是,将显式强制转换为可调用似乎是多余的。对我和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发布于 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表达式是有效的。
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());这并不降低代码的复杂性,但可以编译而不存在任何问题。对我来说,这是一种似曾相识的感觉。当Java5和Generics出现时,程序员不得不在new表达式上重复类型参数,同时简单地将表达式包装到泛型方法中,这证明了推断类型是没有问题的。直到Java 7才允许程序员省略这些不必要的类型参数重复(使用“菱形运算符”)。现在,我们有了类似的情况,将调用表达式包装到另一种方法中,将接收方转换为参数,证明了这种限制是不必要的。所以也许我们可以在Java 10…中消除这个限制。
发布于 2017-10-24 19:33:27
我遇到了同样的问题,并能够通过向map显式指定泛型类型参数来解决这个问题,如下所示:
List<Callable<Object>> checks = l.stream().
<Callable<Object>>map(n -> () -> {
System.out.println(n);
return null;
}).
collect(Collectors.toList());发布于 2014-11-11 20:05:03
我还没有深入研究类型推断如何与lambdas一起工作的确切规则。然而,从一般语言设计的角度来看,编写语言规则并不总是可能的,这样编译器就可以理解我们认为它应该做的一切。我曾经是Ada语言编译器的编译器维护者,我熟悉那里的许多语言设计问题。Ada在很多情况下使用类型推断(如果不查看包含构造的整个表达式,就无法确定构造的类型,我认为这个Java lambda表达式也是如此)。在理论上只有一种可能的解释时,有一些语言规则会导致编译器拒绝某些含糊的表达式。如果我没记错的话,原因之一是有人发现了这样一种情况,一条让编译器计算出正确解释的规则会要求编译器进行17遍表达式,才能正确解释它。
因此,虽然我们可能认为编译器“应该”能够在特定情况下解决问题,但这显然是不可行的。
https://stackoverflow.com/questions/26872827
复制相似问题