这与my answer to "stream reduction incompatible types"有关。我不知道为什么我建议的作品,Holger正确地向我施压。但即使他似乎也没有清楚地解释它为何起作用。所以,让我们将其作为自己的问题:
以下代码无法在javac
中编译(对于下面的ideone链接,这是sun-jdk-1.8.0_51
,每http://ideone.com/faq}:
public <T> Object with(Stream<Predicate<? super T>> predicates) {
return predicates.reduce(Predicate::or);
}
这是正确的:或者说这个流中的两个谓词就像写作一样:
Predicate<? super T> a = null;
Predicate<? super T> b = null;
a.or(b); // Compiler error!
但是,虽然在Predicate::or
方法引用上有原始类型警告,但它确实在intellij中编译。显然,它也会在eclipse中编译(根据原始问题)。
但是这段代码确实:
public <T> Object with(Stream<Predicate<? super T>> predicates) {
return predicates.map(a -> a).reduce(Predicate::or);
// ^----------^ Added
}
尽管我想要尝试这个,但我并不完全清楚为什么这会起作用。我的手写波形解释是.map(a -> a)
就像&#34; cast&#34;,并且类型推断算法更灵活地选择允许reduce
应用的类型。但我不确定那种类型是什么。
请注意,这并不等同于使用.map(Function.identity())
,因为它被限制为返回输入类型。 ideone demo
任何人都可以解释为什么这可以参考语言规范,或者如Holger建议的那样,它是编译器错误吗?
更详细一点:
该方法的返回类型可以更具体一些;我在上面省略了它,以便返回类型上令人讨厌的泛型不会妨碍:
public <T> Optional<? extends Predicate<? super T>> with(
Stream<Predicate<? super T>> predicates) {
return predicates.map(a -> a).reduce(Predicate::or);
}
这是使用-XDverboseResolution=all
进行编译的输出。不完全确定这是否是我可以发布以调试类型推断的最相关的输出;请告知是否有更好的事情:
Interesting.java:5: Note: resolving method <init> in type Object to candidate 0
class Interesting {
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: Object()
Interesting.java:7: Note: resolving method map in type Stream to candidate 0
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <R>map(Function<? super T#1,? extends R>)
(partially instantiated to: (Function<? super Predicate<? super T#2>,? extends Object>)Stream<Object>)
where R,T#1,T#2 are type-variables:
R extends Object declared in method <R>map(Function<? super T#1,? extends R>)
T#1 extends Object declared in interface Stream
T#2 extends Object declared in method <T#2>with(Stream<Predicate<? super T#2>>)
Interesting.java:7: Note: Deferred instantiation of method <R>map(Function<? super T#1,? extends R>)
return predicates.map(a -> a).reduce(Predicate::or);
^
instantiated signature: (Function<? super Predicate<? super T#2>,? extends Predicate<CAP#1>>)Stream<Predicate<CAP#1>>
target-type: <none>
where R,T#1,T#2 are type-variables:
R extends Object declared in method <R>map(Function<? super T#1,? extends R>)
T#1 extends Object declared in interface Stream
T#2 extends Object declared in method <T#2>with(Stream<Predicate<? super T#2>>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object super: T#2 from capture of ? super T#2
Interesting.java:7: Note: resolving method reduce in type Stream to candidate 1
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 not applicable method found: <U>reduce(U,BiFunction<U,? super T,U>,BinaryOperator<U>)
(cannot infer type-variable(s) U
(actual and formal argument lists differ in length))
#1 applicable method found: reduce(BinaryOperator<T>)
#2 not applicable method found: reduce(T,BinaryOperator<T>)
(actual and formal argument lists differ in length)
where U,T are type-variables:
U extends Object declared in method <U>reduce(U,BiFunction<U,? super T,U>,BinaryOperator<U>)
T extends Object declared in interface Stream
Interesting.java:7: Note: resolving method metafactory in type LambdaMetafactory to candidate 0
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: Lookup,String,MethodType,MethodType,MethodHandle,MethodType
with type-args: no arguments
candidates:
#0 applicable method found: metafactory(Lookup,String,MethodType,MethodType,MethodHandle,MethodType)
Interesting.java:7: Note: resolving method metafactory in type LambdaMetafactory to candidate 0
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: Lookup,String,MethodType,MethodType,MethodHandle,MethodType
with type-args: no arguments
candidates:
#0 applicable method found: metafactory(Lookup,String,MethodType,MethodType,MethodHandle,MethodType)
答案 0 :(得分:2)
除非我错过了关于FunctionalInterface推理如何发生的事情,否则很明显你不能在Stream上调用reduce&lt; ? super Predicate&gt;因为它没有足够的类型推断为BinaryOperator。
方法参考隐藏了故事的一个非常重要的部分,即第二个参数。
return predicates.map(a->a).reduce((predicate, other) -> predicate.or(other));
如果删除对map的调用,编译器将无法适当地键入流以满足第二个捕获要求。使用map,编译器被赋予了确定满足捕获所需类型的自由度,但是如果没有具体的泛型绑定,则只能通过对象流来满足两个捕获,这可能是通过map()产生的。 / p>
现在实现的Predicate接口只是构建一个链,但是使用它应该是一个组合实体。假设采用单个参数,但实际上AND和OR的性质需要两个参数而没有类型保证,因为Java的泛型有缺点。通过这种方式,API似乎不如理想设计。
对map()的调用放弃了对显式Stream of Predicates的输入控制,以及编译器可以保证满足所有捕获的控制。
以下两者都满足IDEone中的编译器,在Object的情况下直接引入足够灵活的类型,或在T的情况下引入已知类型。
public <T> Optional<? extends Predicate<? super T>> with(Stream<Predicate<Object>> predicates)
public <T> Optional<? extends Predicate<? super T>> with(Stream<Predicate<T>> predicates)
Java泛型仍然需要一种强制捕获类型等价的方法,因为辅助方法显然是不够的。