为什么添加“.map(a – > a)”允许这个编译?

这与我对“流减少不兼容类型”的回答有关。 我不知道为什么我的建议有效,而霍尔格正确地向我施压。 但即使他似乎也没有清楚解释它为何起作用。 那么,让我们问它自己的问题:

以下代码无法在javacjavac (对于下面的ideone链接,这是sun-jdk-1.8.0_51 ,根据http://ideone.com/faq ):

 public  Object with(Stream<Predicate> predicates) { return predicates.reduce(Predicate::or); } 

这是正确的:或者从这个流中合并两个谓词就像写:

 Predicate a = null; Predicate b = null; a.or(b); // Compiler error! 

但是,它确实在intellij中编译,尽管在Predicate::or方法引用上有原始类型警告。 显然,它也会在eclipse中编译(根据原始问题)。

但是这段代码确实:

 public  Object with(Stream<Predicate> predicates) { return predicates.map(a -> a).reduce(Predicate::or); // ^----------^ Added } 

Ideone演示

尽管我想要尝试这个,但我不清楚为什么这会起作用。 我的手工波形解释是.map(a -> a)就像一个“强制转换”,并且给类型推断算法提供了更多的灵活性来选择允许应用reduce的类型。 但我不确定那种类型到底是什么。

请注意,这不等于使用.map(Function.identity()) ,因为它被约束为返回输入类型。 ideone演示

任何人都可以解释为什么这可以参考语言规范,或者如Holger建议的那样,它是一个编译器错误?


更详细一点:

该方法的返回类型可以更具体一些; 我在上面省略了它,以便返回类型上令人讨厌的generics不会妨碍:

 public  Optional<? extends Predicate> with( Stream<Predicate> predicates) { return predicates.map(a -> a).reduce(Predicate::or); } 

这是使用-XDverboseResolution=all进行编译的输出。 不完全确定这是否是我可以发布以调试类型推断的最相关的输出; 请告知是否有更好的事情:

 Interesting.java:5: Note: resolving method  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:  with type-args: no arguments candidates: #0 applicable method found: map(Function) (partially instantiated to: (Function<? super Predicate,? extends Object>)Stream) where R,T#1,T#2 are type-variables: R extends Object declared in method map(Function) T#1 extends Object declared in interface Stream T#2 extends Object declared in method with(Stream<Predicate>) Interesting.java:7: Note: Deferred instantiation of method map(Function) return predicates.map(a -> a).reduce(Predicate::or); ^ instantiated signature: (Function<? super Predicate,? extends Predicate>)Stream<Predicate> target-type:  where R,T#1,T#2 are type-variables: R extends Object declared in method map(Function) T#1 extends Object declared in interface Stream T#2 extends Object declared in method with(Stream<Predicate>) 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:  with type-args: no arguments candidates: #0 not applicable method found: reduce(U,BiFunction,BinaryOperator) (cannot infer type-variable(s) U (actual and formal argument lists differ in length)) #1 applicable method found: reduce(BinaryOperator) #2 not applicable method found: reduce(T,BinaryOperator) (actual and formal argument lists differ in length) where U,T are type-variables: U extends Object declared in method reduce(U,BiFunction,BinaryOperator) 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) 

除非我错过了关于FunctionalInterface推理如何发生的事情,否则很明显你不能在Stream上调用reduce <? super Predicate>因为它没有足够的类型推断为BinaryOperator。

方法参考隐藏了故事的一个非常重要的部分,即第二个参数。

 return predicates.map(a->a).reduce((predicate, other) -> predicate.or(other)); 

如果删除对map的调用,则编译器没有机会适当地键入流以满足第二个捕获要求。 使用map,编译器被赋予了确定满足捕获所需类型的自由度,但是如果没有具体的generics绑定,则只能通过对象流来满足两个捕获,这可能是通过map()产生的。

现在实现的Predicate接口只是构建一个链,但是使用它应该是一个组合实体。 假设采用单个参数,但实际上AND和OR的性质需要两个参数而没有类型保证,因为Java的generics有缺点。 通过这种方式,API似乎不如理想设计。

对map()的调用放弃了对显式Stream of Predicates的输入控制,以及编译器可以保证满足所有捕获的控制。

以下两者都满足IDEone中的编译器,在Object的情况下直接引入足够灵活的类型,或者在T的情况下引入已知类型。

 public  Optional> with(Stream> predicates) public  Optional> with(Stream> predicates) 

Javagenerics仍然需要一种强制捕获类型等价的方法,因为辅助方法显然是不够的。