运算符

Infra 表达式语言支持以下类型的运算符:

关系运算符

使用标准运算符符号支持关系运算符(等于、不等于、小于、小于或等于、大于和大于或等于)。 这些运算符适用于 Number 类型以及实现 Comparable 的类型。 以下清单显示了一些关系运算符的示例:

  • Java

// 评估结果为 true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// 评估结果为 false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// 评估结果为 true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

// 使用 CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(1) < new CustomValue(2)").getValue(Boolean.class);

针对 null 的大于和小于比较遵循一个简单的规则:null 被视为无(即不是零)。 因此,任何其他值总是大于 nullX > null 总是 true),并且没有其他值小于无(X < null 总是 false)。

如果你更喜欢数字比较,请避免基于数字的 null 比较,而倾向于与零进行比较(例如,X > 0X < 0)。

每个符号运算符也可以指定为纯文本等效项。这避免了所使用的符号在嵌入表达式的文档类型中具有特殊含义的问题 (例如在 XML 文档中)。文本等效项是:

  • lt (<)

  • gt (>)

  • le (<=)

  • ge (>=)

  • eq (==)

  • ne (!=)

所有文本运算符都不区分大小写。

除了标准关系运算符外,SpEL 还支持 betweeninstanceof 和基于正则表达式的 matches 运算符。 以下清单显示了这三个运算符的示例:

  • Java

boolean result;

// 评估结果为 true
result = parser.parseExpression(
    "1 between {1, 5}").getValue(Boolean.class);

// 评估结果为 false
result = parser.parseExpression(
    "1 between {10, 15}").getValue(Boolean.class);

// 评估结果为 true
result = parser.parseExpression(
    "'elephant' between {'aardvark', 'zebra'}").getValue(Boolean.class);

// 评估结果为 false
result = parser.parseExpression(
    "'elephant' between {'aardvark', 'cobra'}").getValue(Boolean.class);

// 评估结果为 true
result = parser.parseExpression(
    "123 instanceof T(Integer)").getValue(Boolean.class);

// 评估结果为 false
result = parser.parseExpression(
    "'xyz' instanceof T(Integer)").getValue(Boolean.class);

// 评估结果为 true
result = parser.parseExpression(
    "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

// 评估结果为 false
result = parser.parseExpression(
    "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

between 运算符的语法是 <input> between {<range_begin>, <range_end>}, 这实际上是 <input> >= <range_begin> && <input> <= <range_end>} 的快捷方式。

因此,1 between {1, 5} 评估为 true,而 1 between {5, 1} 评估为 false

小心基本类型,因为它们会立即装箱为其包装类型。 例如,1 instanceof T(int) 评估为 false,而 1 instanceof T(Integer) 评估为 true

逻辑运算符

SpEL 支持以下逻辑 (boolean) 运算符:

  • and (&&)

  • or (||)

  • not (!)

所有文本运算符都不区分大小写。

下例展示了如何使用逻辑运算符:

  • Java

// -- AND --

// 评估结果为 false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// 评估结果为 true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// 评估结果为 true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// 评估结果为 true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// 评估结果为 false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);

// -- AND and NOT --

String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

字符串运算符

你可以在字符串上使用以下运算符。

  • 连接 (+)

  • 减法 (-)

    • 用于包含单个字符的字符串

  • 重复 (*)

下例展示了 String 运算符的使用:

  • Java

// -- 连接 --

// 评估结果为 "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
    .getValue(String.class);

// -- 字符减法 --

// 评估结果为 'a'
char ch = parser.parseExpression("'d' - 3")
    .getValue(char.class);

// -- 重复 --

// 评估结果为 "abcabc"
String repeated = parser.parseExpression("'abc' * 2")
    .getValue(String.class);

数学运算符

你可以在数字上使用以下运算符,并强制执行标准运算符优先级。

  • 加法 (+)

  • 减法 (-)

  • 自增 (++)

  • 自减 (--)

  • 乘法 (*)

  • 除法 (/)

  • 取模 (%)

  • 指数幂 (^)

除法和取模运算符也可以指定为纯文本等效项。 这避免了所使用的符号在嵌入表达式的文档类型中具有特殊含义的问题(例如在 XML 文档中)。文本等效项是:

  • div (/)

  • mod (%)

所有文本运算符都不区分大小写。

自增和自减运算符可以与可写入的变量或属性一起使用前缀(A`、`--A`)或后缀(`AA--)符号。

下例展示了数学运算符的使用:

  • Java

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// -- 加法 --

int two = parser.parseExpression("1 + 1").getValue(int.class);  // 2

// -- 减法 --

int four = parser.parseExpression("1 - -3").getValue(int.class);  // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(double.class);  // -9000

// -- 自增 --

// Inventor 中的 counter 属性初始值为 0。

// 评估结果为 2; counter 现在是 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);

// 评估结果为 5; counter 现在是 2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);

// -- 自减 --

// Inventor 中的 counter 属性值为 2。

// 评估结果为 6; counter 现在是 1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);

// 评估结果为 5; counter 现在是 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);

// -- 乘法 --

six = parser.parseExpression("-2 * -3").getValue(int.class);  // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(double.class);  // 24.0

// -- 除法 --

int minusTwo = parser.parseExpression("6 / -3").getValue(int.class);  // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(double.class);  // 1.0

// -- 取模 --

int three = parser.parseExpression("7 % 4").getValue(int.class);  // 3

int oneInt = parser.parseExpression("8 / 5 % 2").getValue(int.class);  // 1

// -- 指数幂 --

int maxInt = parser.parseExpression("(2^31) - 1").getValue(int.class);  // Integer.MAX_VALUE

int minInt = parser.parseExpression("-2^31").getValue(int.class);  // Integer.MIN_VALUE

// -- 运算符优先级 --

int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(int.class);  // -21

赋值运算符

要设置属性,请使用赋值运算符 (=)。这通常在调用 setValue 时完成, 但也可以在调用 getValue 时完成。以下清单显示了使用赋值运算符的两种方式:

  • Java

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");

// 或者
String aleks = parser.parseExpression(
    "name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);

重载运算符

默认情况下,SpEL 的 Operation 枚举中定义的数学运算(ADDSUBTRACTDIVIDEMULTIPLYMODULUSPOWER)支持简单类型,如数字。 通过提供 OperatorOverloader 的实现,表达式语言可以支持对其他类型的这些操作。

例如,如果我们想重载 ADD 运算符以允许使用 + 号连接两个列表,我们可以实现一个自定义 OperatorOverloader,如下所示。

pubic class ListConcatenation implements OperatorOverloader {

  @Override
  public boolean overridesOperation(Operation operation, Object left, Object right) {
    return (operation == Operation.ADD &&
        left instanceof List && right instanceof List);
  }

  @Override
  @SuppressWarnings("unchecked")
  public Object operate(Operation operation, Object left, Object right) {
    if (operation == Operation.ADD &&
        left instanceof List list1 && right instanceof List list2) {

      List result = new ArrayList(list1);
      result.addAll(list2);
      return result;
    }
    throw new UnsupportedOperationException(
      "No overload for operation %s and operands [%s] and [%s]"
        .formatted(operation, left, right));
  }
}

如果我们将 ListConcatenation 注册为 StandardEvaluationContext 中的 OperatorOverloader, 我们就可以评估像 {1, 2, 3} + {4, 5} 这样的表达式,如下例所示。

  • Java

StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());

// 评估结果为新列表: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);

OperatorOverloader 不会更改运算符的默认语义。 例如,上例中的 2 + 2 仍然评估为 4

任何使用重载运算符的表达式都无法编译。 有关详细信息,请参阅 编译器限制