请考虑以下代码:
class Predicate {
public boolean eval(EvaluationContext ec) { /* logic here */ }
}
// later ...
List<Predicate> preds = new List<>( /* some predicates here */ );
// now let's use Stream<> to implement the AND logical connective:
// VERSION A:
Boolean resultA = preds.stream()
.map(p -> p.eval(context))
.reduce(Boolean.TRUE, (a,b) -> Boolean.logicalAnd(a,b));
// Oops: the code above doesn't compile ...
// Error: incompatible types: java.lang.Object cannot be converted to boolean
// VERSION B: (add an intermediate variable with explicit type)
Stream<Boolean> v = _children.stream().map(p -> p.eval(context));
Boolean resultB = v.reduce(Boolean.TRUE, (a,b) -> Boolean.logicalAnd(a, b) );
// compiles just fine...
所以,我的问题是:
版本A的结构有什么问题阻止Java编译器正确推断map()的结果类型?这是Java中类型推理算法的限制吗?如果是这样,是否有更好的方法来编写此代码,以便类型推断成功?
答案 0 :(得分:1)
您的代码可以使用jdk1.8.0_05
,jdk1.8.0_20
,jdk1.8.0_25
和jdk1.8.0_40 (beta)
进行编译,并且没有理由认为还有其他版本存在问题。当你修复你编写的代码的其他错误或发布你给编译器的真实代码时,它可能会有所帮助。
E.g。如果new List<>
引用List
且您的示例代码不包含java.util.List
声明,则无法说context
。如果找不到context
,编译器确实会产生“不兼容类型”后续错误,一旦修复了其他错误,该错误就会消失。令人惊讶的是,您的第二个示例使用_children
而不是preds
,因此它来自不同的上下文,其中很可能不存在此类编译器错误。
顺便说一句,你的lambda表达式(a,b) -> Boolean.logicalAnd(a,b)
有点奇怪。使用表达式,例如(a,b) -> a && b
或类似Boolean::logicalAnd
的方法参考。
但无论如何,建议不要使用减少。您可以通过使用短路方法获得更好的性能,例如: _children.stream().allMatch(p -> p.eval(context))
为logical and
,_children.stream().anyMatch(p -> p.eval(context))
为logical or
。
答案 1 :(得分:1)
有什么理由可以使用
将所有谓词组合在一起Predicate<EvaluationContext> finalTest = preds.stream()
.reduce(((p)->true), Predicate::and);
然后你有一个谓词,只需编译一次就可以保存并重复使用它来测试任意数量的上下文。
重新阅读之后,我注意到你的Predicate类不是java.util.function.Predicate。为了做我描述的内容,你需要稍微修改你的Predicate类,使它实现Predicate接口:
public class Predicate implements java.util.function.Predicate<EvaluationContext>{
public boolean eval(EvaluationContext ec) { /* logic here */ }
@Override
public boolean test(EvaluationContext t) {
return eval(t);
}
}