解决Java布尔表达式树中接口方法参数的歧义问题
问题
我需要在Java中运行由布尔表达式生成的布尔运算树,让它迭代执行并返回布尔值,示例表达式如下:
boolean b = AND(isNumberEven("4"), AND(isNumberOdd("7"), isNumberEven("6")));
公司要求支持多种节点类型:双分支的逻辑运算符节点(如And、Or),以及作为叶子节点的属性检查节点(如isSomething、hasInArray)。
我尝试设计两个接口分别对应逻辑运算符与属性检查,但编写的代码存在泛型类型问题,编译器无法推断fire()方法的正确类型。我习惯了JavaScript的动态类型,想知道如何在Java中正确实现这种树形布尔运算结构,解决接口方法参数的歧义问题?
附上我尝试的代码实现:
class PropertyCheckingInput { String s; // 模拟属性,这个对象是我们要验证属性的目标(比如判断这个字符串是否为偶数) PropertyCheckingInput(...){...} // 包含所有参数的构造方法 } interface PropertyChecking { boolean fire(PropertyCheckingInput i); } class LogicOperatorInput< T extends LogicOperator or PropertyChecking, // 可以是两者之一 Ti extends LogicOperatorInput or PropertyCheckingInput > { T left; Ti leftInput; T right; Ti rightInput; // 可能leftInput的类型应该和left的类型匹配,但我不知道怎么用Java泛型强制约束 LogicOperatorInput(...){...} // 包含所有参数的构造方法 } interface LogicOperator{ boolean fire(LogicOperatorInput i); }
And类实现示例:
class And implements LogicOperator { boolean fire(LogicOperatorInput i) { i.left.fire(i.leftInput) && i.right.fire(i.rightInput); } And() {} public static void main(String[] args) { // 表达式:isNumberEven("4") AND isNumberOdd("7") AND isNumberEven("6") boolean b = new And().fire(new LogicOperatorInput( new isNumberEven(), new PropertyCheckingInput("4"), new And(), new LogicOperatorInput( new isNumberOdd(), new PropertyCheckingInput("7"), new isNumberEven(), new PropertyCheckingInput("6")) )); System.out.println(b); } }
解决方案
核心思路是统一所有节点的接口,让逻辑运算符和属性检查节点都实现同一个顶层接口,这样整个运算树的节点可以统一处理,避免泛型歧义。
1. 定义统一的布尔节点接口
创建一个顶层接口BooleanNode,所有节点(包括逻辑运算符和属性检查器)都实现它,这样不管是分支节点还是叶子节点,都可以用同一个方法执行:
@FunctionalInterface interface BooleanNode { boolean evaluate(); }
使用@FunctionalInterface是因为每个节点只需要一个执行方法,也方便后续用Lambda简化实现。
2. 实现属性检查叶子节点
属性检查节点作为叶子节点,直接封装检查逻辑和输入参数,不需要依赖外部输入:
// 通用的属性检查输入类,可根据实际需求扩展字段 class PropertyInput { private final String value; public PropertyInput(String value) { this.value = value; } public String getValue() { return value; } } // 检查数字是否为偶数的节点 class IsNumberEven implements BooleanNode { private final PropertyInput input; public IsNumberEven(PropertyInput input) { this.input = input; } @Override public boolean evaluate() { try { int num = Integer.parseInt(input.getValue()); return num % 2 == 0; } catch (NumberFormatException e) { return false; } } } // 检查数字是否为奇数的节点 class IsNumberOdd implements BooleanNode { private final PropertyInput input; public IsNumberOdd(PropertyInput input) { this.input = input; } @Override public boolean evaluate() { try { int num = Integer.parseInt(input.getValue()); return num % 2 != 0; } catch (NumberFormatException e) { return false; } } }
3. 实现逻辑运算符分支节点
逻辑运算符节点(And、Or)作为分支节点,持有左右两个子节点(可以是任何BooleanNode类型,包括其他逻辑节点或属性检查节点):
// And逻辑运算符节点 class And implements BooleanNode { private final BooleanNode left; private final BooleanNode right; public And(BooleanNode left, BooleanNode right) { this.left = left; this.right = right; } @Override public boolean evaluate() { // 短路求值,和Java原生&&行为一致 return left.evaluate() && right.evaluate(); } } // Or逻辑运算符节点 class Or implements BooleanNode { private final BooleanNode left; private final BooleanNode right; public Or(BooleanNode left, BooleanNode right) { this.left = left; this.right = right; } @Override public boolean evaluate() { // 短路求值,和Java原生||行为一致 return left.evaluate() || right.evaluate(); } }
4. 测试运行
现在可以按照你想要的方式构建布尔运算树并执行,代码结构和示例表达式完全对应:
public class BooleanTreeDemo { public static void main(String[] args) { // 构建表达式:isNumberEven("4") AND (isNumberOdd("7") AND isNumberEven("6")) BooleanNode tree = new And( new IsNumberEven(new PropertyInput("4")), new And( new IsNumberOdd(new PropertyInput("7")), new IsNumberEven(new PropertyInput("6")) ) ); boolean result = tree.evaluate(); System.out.println(result); // 输出 true } }
方案优势
- 统一接口消除泛型歧义:所有节点都实现
BooleanNode,不需要区分逻辑节点和属性节点的输入类型,每个节点自己封装所需的输入和逻辑。 - 符合Java静态类型要求:每个节点职责明确,编译器可以清晰推断类型,避免原代码中
LogicOperatorInput的泛型混乱。 - 扩展性强:新增逻辑运算符(如Not)或属性检查节点时,只需要实现
BooleanNode接口即可,无需修改现有结构。
内容的提问来源于stack exchange,提问作者simondx
相关产品推荐
相关产品推荐

