为什么要添加“.map(a - > a)”允许这个编译? [英] Why does adding ".map(a -> a)" allow this to compile?
问题描述
这与我对流式缩减不兼容类型的回答有关。我不知道为什么我建议的作品,并 Holger 正确地向我强调这一点。但即使他似乎没有一个明确的解释,为什么它的作品。所以,让我们问它作为自己的问题:
下面的代码不能在 javac
(对于下面的ideone链接,这是 sun-jdk-1.8.0_51
,每个 http://ideone.com/faq ):
public< T>带有(Stream< Predicate< ;? super T>>谓词)的对象{
返回predicates.reduce(Predicate :: or);
$ / code>
正确如此:将这个流中的两个谓词组合起来就像写:
谓词<?超T> a = null;
谓词<?超T> b = null;
a.or(b); //编译器错误!
然而,它会在intellij中编译,尽管在 Predicate ::或
方法参考。显然,它也会在eclipse中编译(根据原始问题)。
但是这段代码确实如此:
public< T>带有(Stream< Predicate<?super T>>谓词)的对象{
返回predicates.map(a - > a).reduce(Predicate :: or);
// ^ ---------- ^新增
}
<尽管我想这样做,但我不清楚为什么这会起作用。我的手边的解释是,
.map(a - > a)
的作用类似于cast,并且使类型推断算法更灵活地选择类型它允许应用 reduce
。但我不确定究竟是什么类型。 请注意,这不等同于使用 .map(Function.identity() )
,因为这被限制为返回输入类型。 ideone demo
可以任何人都解释了为什么这会引用语言规范,或者,如Holger所建议的那样,它是一个编译器错误?
<详细一点:
该方法的返回类型可以更具体一些;我在上面省略了它,以便返回类型的讨厌泛型不会妨碍:
public< T>可选< ;?扩展谓词<? super T>> (
Stream< Predicate<?super T>>谓词){
返回predicates.map(a - > a).reduce(Predicate :: or);
}
这是使用 -XDverboseResolution =所有
。不完全确定这是否是我可以用来调试类型推断的最相关的输出;请告知是否有更好的方法:
Interesting.java:5:注意:解析方法< init> Object类型到候选类型0
类有趣的{
^
阶段:BASIC
with actuals:无参数
with type-args:无参数
候选:
#0找到的适用方法:Object()
Interesting.java:7:注意:将类型流中的方法映射解析为候选0
返回predicates.map(a - > a).reduce(Predicate :: or);
^
阶段:基本
包含实际值:<无>
with type-args:no arguments
candidates:
#0找到适用的方法:< R> map(Function< ;? super T#1,?extends R>)
(部分实例化为:(函数<超级谓词<超级T#2>,扩展对象>)流< Object>)
其中R,T#1,T#2是类型变量:
R extends在方法< R> map中声明的对象(函数< ;? super T#1,?extends R>)
T#1 extends在接口Stream中声明的对象
T#2 extends对象声明(Stream< Predicate< super T#2>)
Interesting.java:7:注意:方法< R>映射的延迟实例化(函数< ;?super T#1,?extends R>)
返回predicates.map(a - > a).reduce(Predicate :: or);
^
实例化的签名:(函数<超级谓词<超级T#2>,?扩展谓词< CAP#1>)流<谓词< CAP#1>>
target-type:< none>
其中R,T#1,T#2是类型变量:
R extends在方法< R>映射中声明的对象(Function< super T#1,?extends R>)
T#1 extends接口Stream中声明的对象
T#2扩展在< T#2>方法中声明的对象(Stream< Predicate< ;? super T#2>>)
其中CAP #1是一个新鲜的类型变量:
CAP#1扩展Object super:T#2捕获? super T#2
Interesting.java:7:注意:解析方法将类型为Stream的元素减少为候选元素1
返回predicates.map(a - > a).reduce(Predicate ::要么);
^
阶段:基本
包含实际值:<无>
with type-args:no arguments
candidate:
#0不适用找到的方法:< U> reduce(U,BiFunction< U,?super T,U> BinaryOperator< U> ;)
(不能推断类型变量U
(实际和形式参数列表的长度不同))
#1找到的适用方法:reduce(BinaryOperator< T>)
#2不适用找到的方法reduce(T,BinaryOperator< T>)
(实际和形式参数列表长度不同)
其中U,T是类型变量:
U (U,BiFunction< U,?super T,U>,BinaryOperator< U>)
T扩展在接口Stream
中声明的对象有趣的。 java:7:注意:将类型LambdaMetafactory中的方法metafactory解析为候选0
返回predicates.map(a - > a).reduce(Predicate :: or);
$
阶段:BASIC
带实际值:查找,字符串,方法类型,方法类型,方法处理,方法类型
带有类型参数:无参数
候选者:
#0找到的适用方法:metafactory(Lookup,String,MethodType,MethodType,MethodHandle,MethodType)
Interesting.java:7:注意:将类型LambdaMetafactory中的方法metafactory解析为候选0
返回predicates.map(a - > a).reduce(Predicate :: or);
$
阶段:BASIC
带实际值:查找,字符串,方法类型,方法类型,方法处理,方法类型
带有类型参数:无参数
候选者:
#0找到适用的方法:metafactory(Lookup,String,MethodType,MethodType,MethodHandle,MethodType)
除非我在FunctionalInterface推理的发生方面丢失了一些东西,否则很明显你不能在Stream上调用Reduce< ? super predicate>,因为它没有足够的类型被推断为BinaryOperator。
方法参考隐藏了故事中非常重要的部分,即第二个参数。
p> return predicates.map(a-> a).reduce((predicate,other) - > predicate.or(other) );
如果您删除对映射的调用,编译器将无法正确输入流满足第二个捕获要求。在map中,编译器给定了确定满足捕获所需的类型的纬度,但是如果没有泛型的具体绑定,那么两个捕获只能通过可能通过map()得到的对象流满足。 / p>
现在实现的Predicate接口只是构建一个链,但预计这个用途是一个合成实体。假定它只接受一个参数,但实际上由于Java泛型的缺点,AND和OR的性质需要两个参数而没有类型保证。通过这种方式,API似乎不是理想的设计。
对map()的调用将输入的控制从显式的Predicates流转换为编译器可以保证会满足所有的捕获。
以下两个都满足 Java泛型仍然需要强制捕获类型等价,因为辅助方法显然是不够的。 This is related to my answer to "stream reduction incompatible types". I don't know why what I suggested works, and Holger rightly pressed me on this. But even he doesn't seem to have a clear explanation for why it works. So, let's ask it as its own question: The following code does not compile in And rightly so: or-ing together two predicates from this stream is like writing: However, it does compile in intellij, although with a raw type warning on the But this code does: Despite the fact I thought to try this, it's not exactly clear to me why this would work. My hand-wavy explanation is that Note that this isn't equivalent to using Can anybody explain why this works with reference to the language spec, or if, as suggested by Holger, it is a compiler bug? A bit more detail: The return type of the method can be made a bit more specific; I omitted it above so that the nasty generics on the return type wouldn't get in the way: This is the output of compiling with
Unless I am missing something in how FunctionalInterface inferences occur, it seems pretty obvious that you can't call reduce on a Stream < ? super Predicate > because it does not have sufficient typing to be inferred as a BinaryOperator. The method reference hides a very important part of the story, the second parameter. If you remove the call to map, the compiler does not have the opportunity to type the stream appropriately to satisfy the second capture requirements. With map the compiler is given the latitude to determine the types required to satisfy the captures, but without a concrete binding of the generics the two captures can only be satisfied with a Stream of Object which is likely what would result through map(). The Predicate interface as implemented now is simply building a chain, but the use is expected to be a composed entity. It is assumed to take a single parameter, but in fact the nature of AND and OR require two parameters without a type guarantee because of the shortcomings of Java's generics. In this way the API seems to be less than ideally designed. The call to map() cedes the control of the typing, from the explicit Stream of Predicates, to one the compiler can guarantee will satisfy all captures. The following both satisfy the compiler in IDEone, by directly inducing a flexible enough type in the case of Object, or a known type in the case of T. Java generics still need a way to force capture type equivalence, as the helper methods are clearly not enough. 这篇关于为什么要添加“.map(a - > a)”允许这个编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
public< T>可选< ;?扩展谓词<? super T>> (Stream< Predicate< Object>>谓词)
public< T>可选< ;?扩展谓词<? super T>>与(Stream< Predicate< T>谓词)
javac
(for the links to ideone below, this is sun-jdk-1.8.0_51
, per 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
method reference. Apparently, it would also compile in eclipse (according to the original question).public <T> Object with(Stream<Predicate<? super T>> predicates) {
return predicates.map(a -> a).reduce(Predicate::or);
// ^----------^ Added
}
.map(a -> a)
acts like a "cast", and gives the type inference algorithm a bit more flexibility to pick a type which allows the reduce
to be applied. But I'm not sure exactly what that type is..map(Function.identity())
, because that is constrained to return the input type. ideone demo
public <T> Optional<? extends Predicate<? super T>> with(
Stream<Predicate<? super T>> predicates) {
return predicates.map(a -> a).reduce(Predicate::or);
}
-XDverboseResolution=all
. Not entirely sure if this is the most relevant output I can post to debug the type inference; please advise if there is something better: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)
return predicates.map(a->a).reduce((predicate, other) -> predicate.or(other));
public <T> Optional<? extends Predicate<? super T>> with(Stream<Predicate<Object>> predicates)
public <T> Optional<? extends Predicate<? super T>> with(Stream<Predicate<T>> predicates)