涉及布尔值的Java 8类型推断

时间:2014-12-06 01:13:15

标签: java generics compiler-errors java-8 type-inference

请考虑以下代码:

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中类型推理算法的限制吗?如果是这样,是否有更好的方法来编写此代码,以便类型推断成功?

2 个答案:

答案 0 :(得分:1)

您的代码可以使用jdk1.8.0_05jdk1.8.0_20jdk1.8.0_25jdk1.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);
    }
}