两种Lambda创建Predicate的写法差异及编译失败原因咨询
咱们先从Predicate.isEqual的方法签名入手,它的定义是:
static <T> Predicate<T> isEqual(Object targetRef)
这个静态方法的泛型参数<T>是根据调用时的上下文来推断的,这就是两种写法差异的核心。
写法1编译失败的原因
看这句代码:
Predicate<Integer> predicate = Predicate.isEqual(0).or(Predicate.isEqual(1));
编译器在处理链式调用时,会先单独解析第一个Predicate.isEqual(0)。这时候,因为还没看到后续的.or()调用和左边的赋值目标类型,它只能根据传入的参数0(虽为Integer类型,但方法参数是Object)推断泛型<T>为Object,所以第一个isEqual返回的是Predicate<Object>。
接下来调用.or()方法时,传入的Predicate.isEqual(1)也会被编译器推断为Predicate<Object>(因为.or()的参数需要和前面的Predicate类型匹配),所以整个链式调用的结果是Predicate<Object>,而你要把它赋值给Predicate<Integer>,类型不兼容,自然编译报错。
写法2能正常运行的原因
再看分步写的代码:
Predicate<Integer> pred21 = Predicate.isEqual(0); Predicate<Integer> pred22 = pred21.or(Predicate.isEqual(1));
第一步,当你把Predicate.isEqual(0)赋值给Predicate<Integer>类型的变量时,编译器明确知道泛型<T>应该是Integer,所以pred21是Predicate<Integer>类型。
第二步调用.or()方法时,pred21的.or()方法签名是default Predicate<T> or(Predicate<? super T> other),这里的T是Integer,所以参数other可以是Predicate<? super Integer>。这时候传入的Predicate.isEqual(1)会根据这个上下文推断泛型<T>为Integer,返回Predicate<Integer>,完全符合.or()的参数要求,最终返回的结果也是Predicate<Integer>,可以正常赋值给pred22。
优化写法1的小技巧
如果想保持单语句写法,你可以显式指定泛型参数,帮编译器明确类型:
Predicate<Integer> predicate = Predicate.<Integer>isEqual(0).or(Predicate.isEqual(1));
这样第一个isEqual的泛型<T>被指定为Integer,后续的.or()也会沿用这个类型,就能正常编译了。
内容的提问来源于stack exchange,提问作者daniel

