简体   繁体   English

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

[英]Why does adding “.map(a -> a)” allow this to compile?

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 javac (for the links to ideone below, this is sun-jdk-1.8.0_51 , per http://ideone.com/faq ): 以下代码无法在javacjavac (对于下面的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);
}

And rightly so: or-ing together two predicates from this stream is like writing: 这是正确的:或者将这个流中的两个谓词组合在一起就像写:

Predicate<? super T> a = null;
Predicate<? super T> b = null;
a.or(b);  // Compiler error!

However, it does compile in intellij, although with a raw type warning on the Predicate::or method reference. 但是,它确实在intellij中编译,尽管在Predicate::or方法引用上有原始类型警告。 Apparently, it would also compile in eclipse (according to the original question). 显然,它也会在eclipse中编译(根据原始问题)。

But this code does: 但是这段代码确实:

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

Ideone demo Ideone演示

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 .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. 我的手工波形解释是.map(a -> a)就像一个“强制转换”,并为类型推断算法提供了更多的灵活性来选择允许应用reduce的类型。 But I'm not sure exactly what that type is. 但我不确定那种类型到底是什么。

Note that this isn't equivalent to using .map(Function.identity()) , because that is constrained to return the input type. 请注意,这不等于使用.map(Function.identity()) ,因为它被约束为返回输入类型。 ideone demo ideone演示

Can anybody explain why this works with reference to the language spec, or if, as suggested by Holger, it is a compiler bug? 任何人都可以解释为什么这可以参考语言规范,或者如Holger建议的那样,它是一个编译器错误?


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: 我在上面省略了它,以便返回类型上令人讨厌的泛型不会妨碍:

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

This is the output of compiling with -XDverboseResolution=all . 这是使用-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)

Unless I am missing something in how FunctionalInterface inferences occur, it seems pretty obvious that you can't call reduce on a Stream < ? 除非我错过了关于FunctionalInterface推理如何发生的事情,否则很明显你不能在Stream上调用reduce <? super Predicate > because it does not have sufficient typing to be inferred as a BinaryOperator. super Predicate>因为它没有足够的类型推断为BinaryOperator。

The method reference hides a very important part of the story, the second parameter. 方法参考隐藏了故事的一个非常重要的部分,即第二个参数。

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

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

The Predicate interface as implemented now is simply building a chain, but the use is expected to be a composed entity. 现在实现的Predicate接口只是构建一个链,但是使用它应该是一个组合实体。 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. 假设采用单个参数,但实际上AND和OR的性质需要两个参数而没有类型保证,因为Java的泛型有缺点。 In this way the API seems to be less than ideally designed. 通过这种方式,API似乎不如理想设计。

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. 对map()的调用将显式的Stream of Predicates的输入控制转换为编译器可以保证满足所有捕获的控制。

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. 以下两者都满足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 generics still need a way to force capture type equivalence, as the helper methods are clearly not enough. Java泛型仍然需要一种强制捕获类型等价的方法,因为辅助方法显然是不够的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM