Aviator表达式语法基础和Java实战表达式(电商应用)

Aviator 表达式语法

Aviator 语法主要借鉴了 Java 语言,因此对于 Java 开发者来说非常容易上手。

1. 基本运算

  • 算术运算:

    aviatorscript 复制代码
    1 + 2 - 3 * 4 / 5 % 6 // 加、减、乘、除、取模
    -10 // 负数
  • 比较运算:

    aviatorscript 复制代码
    a > b
    a < b
    a >= b
    a <= b
    a == b // 相等
    a != b // 不等
  • 逻辑运算:

    aviatorscript 复制代码
    a && b // 逻辑与 (and)
    a || b // 逻辑或 (or)
    !a     // 逻辑非 (not)
  • 位运算:

    aviatorscript 复制代码
    a & b  // 按位与
    a | b  // 按位或
    a ^ b  // 按位异或
    ~a     // 按位非
    a << b // 左移
    a >> b // 右移
    a >>> b // 无符号右移

2. 数据类型和字面量

  • 整数:

    aviatorscript 复制代码
    123
    0xFF // 十六进制
    077 // 八进制 (注意:在 Aviator 5.0+ 中,推荐使用 0o77 表示八进制)
  • 浮点数 (double):

    aviatorscript 复制代码
    123.45
    .5
    1.2e3 // 科学计数法
  • 长整数 (long):

    aviatorscript 复制代码
    123L // 以 L 结尾表示长整数
  • 布尔值:

    aviatorscript 复制代码
    true
    false
  • 字符串:

    aviatorscript 复制代码
    "hello world"
    'hello world' // 单引号和双引号都支持
    "a\"b" // 转义字符
  • BigDecimal (高精度浮点数):

    aviatorscript 复制代码
    1.23M // 以 M 结尾表示 BigDecimal 类型
    100M  // 整数也可以是 BigDecimal 类型
  • nil (空值):

    aviatorscript 复制代码
    nil // 相当于 Java 的 null
  • 日期:

    aviatorscript 复制代码
    "2023-01-01 12:00:00" // 字符串形式会被自动解析为 Date 类型(需要配置日期格式化器)
    // 通常通过传入 Date 类型的变量来处理日期

3. 变量引用

  • 直接引用:

    aviatorscript 复制代码
    myVariable // 引用 Map 中 key 为 "myVariable" 的值
  • 访问对象属性:

    aviatorscript 复制代码
    user.name // 访问 user 对象的 name 属性(等同于 user.getName())
    order.customer.address.city // 链式访问
  • 访问 Map 元素:

    aviatorscript 复制代码
    map.key // 访问 Map 中 key 为 "key" 的值 (等同于 map.get("key"))
    // 注意:如果 key 是非法的变量名(如包含特殊字符),需要用方括号:
    map['my-key']

4. 条件表达式 (三元运算符)

  • 基本形式:

    aviatorscript 复制代码
    booleanCondition ? valueIfTrue : valueIfFalse

    示例:

    aviatorscript 复制代码
    score >= 60 ? "及格" : "不及格"

5. if-else 语句 (代码块)

  • 基本形式:

    aviatorscript 复制代码
    if (condition) {
        // block for true
    } else {
        // block for false
    }
  • 嵌套 if-else if-else

    aviatorscript 复制代码
    if (age < 18) {
        "青少年"
    } else {
        if (age >= 18 && age < 60) {
            "成年人"
        } else {
            "老年人"
        }
    }

    注意: Aviator 的 else if 实际上是 else { if { ... } else { ... } } 这种嵌套形式。每个 ifelse 后都需要一个 {} 包裹的代码块。

6. 内置函数

Aviator 提供了丰富的内置函数,例如:

  • 数学函数:

    aviatorscript 复制代码
    math.abs(-10)
    math.max(a, b)
    math.min(a, b)
    math.round(3.14)
    math.pow(2, 3)
    math.sqrt(9)
  • 字符串函数:

    aviatorscript 复制代码
    string.length("hello")
    string.contains("hello", "ell")
    string.startsWith("hello", "he")
    string.endsWith("hello", "lo")
    string.substring("hello", 1, 3) // 从索引1开始,长度为3
    string.split("a,b,c", ",")
  • 集合/序列操作:

    • count (统计元素数量):

      aviatorscript 复制代码
      count([1, 2, 3]) // 返回 3
      count(users) // 如果 users 是 List/Set,返回其大小
    • **isEmpty (判断是否为空):

      aviatorscript 复制代码
      isEmpty([]) // 返回 true
      isEmpty(nil) // 返回 true
    • **include (判断是否包含):

      scss 复制代码
      include([1, 2, 3], 2) // 返回 true
      include(myString, "sub") // 字符串包含子串
    • 序列操作:

      aviatorscript 复制代码
      seq.add([1,2], 3) // 返回 [1,2,3]
      seq.remove([1,2,3], 2) // 返回 [1,3]
      seq.contains([1,2,3], 2) // 返回 true
      seq.size([1,2,3]) // 返回 3
  • **类型转换函数:

    aviatorscript 复制代码
    long(10.5) // 转换为 long
    double(10) // 转换为 double
    str(123) // 转换为字符串
    // 更多转换函数:bigdec(), date() 等
  • **其他常用函数:

    aviatorscript 复制代码
    type(obj) // 获取变量的类型
    nil_or_empty(obj) // 判断是否为 nil 或空(字符串、集合、Map等)
    // ...还有很多,请查阅 Aviator 官方文档

7. 自定义函数

你可以通过 AviatorEvaluator.addFunction() 方法注册自定义的 Java 函数,然后在表达式中像内置函数一样调用。

java 复制代码
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.runtime.type.AviatorObject;

import java.util.Map;

// 自定义加法函数
class MyAddFunction extends AbstractFunction {
    @Override
    public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
        Number num1 = FunctionUtils.getNumberValue(arg1, env);
        Number num2 = FunctionUtils.getNumberValue(arg2, env);
        return AviatorLong.valueOf(num1.longValue() + num2.longValue());
    }

    @Override
    public String getName() {
        return "myAdd"; // 函数名
    }
}

// 在 main 方法中注册
// AviatorEvaluator.addFunction(new MyAddFunction());

// 在表达式中调用
// myAdd(a, b)

8. 集合(List)和 Map 字面量

Aviator 5.0+ 支持 List 和 Map 的字面量表示:

  • List 字面量:

    csharp 复制代码
    [1, 2, "hello", true]
    [1 + 2, max(a, b)] // 元素可以是表达式
  • **Map 字面量:

    shell 复制代码
    #{ "name": "张三", "age": 30, "isValid": true }
    #{ key1: value1, key2: value2 } // key 也可以不加引号,如果它符合变量命名规则

9. 序列操作 (Seq 模块)

Aviator 5.0+ 引入了强大的序列操作,支持对 List、Set、数组等进行过滤、求和、映射等操作。

  • **过滤 (filter):

    scss 复制代码
    filter(users, "user.age > 18 && user.gender == 'male'") // 过滤 age > 18 且 gender 为 male 的用户
  • **映射 (map):

    c 复制代码
    map(users, "user.name") // 提取所有用户的 name 属性
  • 求和 (sum):

    scss 复制代码
    sum(items, "item.price * item.quantity") // 计算所有商品的总价
  • **所有满足 (every):

    scss 复制代码
    every(numbers, "num > 0") // 判断所有数字是否都大于0
  • **任一满足 (some):

    scss 复制代码
    some(users, "user.vip == true") // 判断是否存在VIP用户
  • **排序 (sort):

    scss 复制代码
    sort(products, "product.price desc") // 按价格降序排序

    注意: 序列操作的第二个参数是一个表达式字符串,其中可以通过 element 或你定义的变量名来引用当前元素。

10. lambda 表达式 (匿名函数)

Aviator 5.0+ 支持 lambda 表达式,可以作为函数参数传递。

python 复制代码
filter(users, lambda user : user.age > 18 end) // 等同于上面的 filter 示例
map(items, lambda item : item.price * item.quantity end)

其中 lambda arg1, arg2 : expression end 定义了一个匿名函数。

11. 错误处理

Aviator 表达式在执行过程中如果遇到错误(如类型不匹配、变量不存在),会抛出 com.googlecode.aviator.exception.ExpressionRuntimeException 或其子类。


使用示例总结

一个典型的 Aviator 表达式使用流程:

  1. 定义表达式字符串。
  2. 通过 AviatorEvaluator.compile(expressionString) 编译表达式。 (强烈推荐,特别是重复执行的表达式)
  3. 创建环境变量 Map<String, Object>,将表达式中需要用到的变量放入其中。
  4. 通过 compiledExpression.execute(env) 执行表达式,获取结果。

数据类型

  • Number类型:数字类型,支持两种类型,分别对应Java的Long和Double,也就是说任何整数都将被转换为Long,而任何浮点数都将被转换 为Double,包括用户传入的数值也是如此转换。不支持科学计数法,仅支持十进制。如-1、100、2.3等。
  • String类型:字符串类型,单引号或者双引号括起来的文本串,如'helloworld',变量如果传入的是String或者Character也将转为String类型。任何类型和 String 相加还是 String。
  • Bool类型:常量true和false,表示真值和假值,与java的Boolean.TRUE和Boolean.False对应。
  • Pattern类型: 类似Ruby、perl的正则表达式,以//括起来的字符串,如/\d+/,内部实现为java.util.Pattern。
  • 变量类型:与Java的变量命名规则相同,变量的值由用户传入,如"a"、"b"等
  • nil类型:常量nil,类似java中的null,但是nil比较特殊,nil不仅可以参与==、!=的比较,也可以参与>、>=、<、<=的比较,Aviator规定任何类型 都n大于nil除了nil本身,nil==nil返回true。用户传入的变量值如果为null,那么也将作为nil处理,nil打印为null。

实战场景

该场景为电商系统中的价格计算,在用户结账的时候会涉及许多内容,包括是不是会员、是什么等级的会员、有无优惠卷、当前的营销活动等内容,最终的价格受这些因素的影响。以下,选取了三个场景进行模拟。

java 复制代码
package cn.srw;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {

        // 1. 基础价格表达式 (商品单价 * 购买数量)
        String basePriceExp = "price * quantity";

        // ! 加M表示告诉表达式引擎 返回的是一个BigDecimal对象
        // 2. 会员折扣表达式 (根据会员等级应用折扣)
        String memberDiscountExp = "if (memberLevel == 'GOLD') { basePrice * 0.9M } " +
                "else { " +
                "  if (memberLevel == 'PLATINUM') { basePrice * 0.8M } " +
                "  else { basePrice } " +
                "}";

        // 3. 满减活动表达式 (根据满足的金额减去相应金额)
        String fullReductionExp = "if (totalAfterMemberDiscount >= 500) { totalAfterMemberDiscount - 60M } " +
                "else { " +
                "  if (totalAfterMemberDiscount >= 200) { totalAfterMemberDiscount - 20M } " +
                "  else { totalAfterMemberDiscount } " +
                "}";

        // 4. 最终价格表达式 (减去优惠券面值)
        String finalPriceExp = "totalAfterFullReduction - couponValue";

        // --- 预编译表达式 (推荐做法,提高性能) ---
        Expression compiledBasePriceExp = AviatorEvaluator.compile(basePriceExp);
        Expression compiledMemberDiscountExp = AviatorEvaluator.compile(memberDiscountExp);
        Expression compiledFullReductionExp = AviatorEvaluator.compile(fullReductionExp);
        Expression compiledFinalPriceExp = AviatorEvaluator.compile(finalPriceExp);

        // --- 模拟计算场景 ---

        // 场景1: 普通用户,无优惠券
        System.out.println("--- 场景1: 普通用户,无优惠券 ---");
        Map<String, Object> env1 = new HashMap<>();
        env1.put("price", new BigDecimal("100"));
        env1.put("quantity", 3);
        env1.put("memberLevel", "NONE"); // 普通用户
        env1.put("couponValue", new BigDecimal("0"));

        calculateAndPrintPrice(env1, compiledBasePriceExp, compiledMemberDiscountExp, compiledFullReductionExp, compiledFinalPriceExp);
        // 预期结果: 100 * 3 = 300; 300 >= 200 减 20 = 280; 280 - 0 = 280

        System.out.println("\n--- 场景2: 黄金会员,有优惠券 ---");
        // 场景2: 黄金会员,有优惠券
        Map<String, Object> env2 = new HashMap<>();
        env2.put("price", new BigDecimal("100"));
        env2.put("quantity", 5);
        env2.put("memberLevel", "GOLD"); // 黄金会员
        env2.put("couponValue", new BigDecimal("10"));

        calculateAndPrintPrice(env2, compiledBasePriceExp, compiledMemberDiscountExp, compiledFullReductionExp, compiledFinalPriceExp);
        // 预期结果: 100 * 5 = 500; 黄金会员 500 * 0.9 = 450; 450 >= 200 减 20 = 430; 430 - 10 = 420

        System.out.println("\n--- 场景3: 白金会员,大额订单,无优惠券 ---");
        // 场景3: 白金会员,大额订单,无优惠券
        Map<String, Object> env3 = new HashMap<>();
        env3.put("price", new BigDecimal("200"));
        env3.put("quantity", 4);
        env3.put("memberLevel", "PLATINUM"); // 白金会员
        env3.put("couponValue", new BigDecimal("0"));

        calculateAndPrintPrice(env3, compiledBasePriceExp, compiledMemberDiscountExp, compiledFullReductionExp, compiledFinalPriceExp);
        // 预期结果: 200 * 4 = 800; 白金会员 800 * 0.8 = 640; 640 >= 500 减 60 = 580; 580 - 0 = 580

    }

    /**
     * 计算并打印价格
     *
     * @param env                       环境变量 Map
     * @param compiledBasePriceExp      基础价格表达式
     * @param compiledMemberDiscountExp 会员折扣表达式
     * @param compiledFullReductionExp  满减活动表达式
     * @param compiledFinalPriceExp     最终价格表达式
     */
    private static void calculateAndPrintPrice(
            Map<String, Object> env,
            Expression compiledBasePriceExp,
            Expression compiledMemberDiscountExp,
            Expression compiledFullReductionExp,
            Expression compiledFinalPriceExp) {

        // 1. 计算基础价格
        BigDecimal basePrice = (BigDecimal) compiledBasePriceExp.execute(env);
        env.put("basePrice", basePrice); // 将结果放入环境变量供后续表达式使用
        System.out.println("  基础价格: " + basePrice);

        // 2. 计算会员折扣后的价格
        BigDecimal totalAfterMemberDiscount = (BigDecimal) compiledMemberDiscountExp.execute(env);
        env.put("totalAfterMemberDiscount", totalAfterMemberDiscount);
        System.out.println("  会员折扣后价格: " + totalAfterMemberDiscount);

        // 3. 计算满减活动后的价格
        BigDecimal totalAfterFullReduction = (BigDecimal) compiledFullReductionExp.execute(env);
        env.put("totalAfterFullReduction", totalAfterFullReduction);
        System.out.println("  满减活动后价格: " + totalAfterFullReduction);

        // 4. 计算最终支付价格
        BigDecimal finalPrice = (BigDecimal) compiledFinalPriceExp.execute(env);
        System.out.println("  最终支付价格: " + finalPrice);
    }
}
相关推荐
Hockor2 分钟前
写给前端的 Python 教程一
前端·后端·python
用户131517382561514 分钟前
线上发现 Redis 机器爆了,如何优化?
后端
David爱编程15 分钟前
深入理解容器运行背后的“魔法秘籍”
后端·docker·容器
开开心心就好16 分钟前
小巧实用,Windows文件夹着色软件推荐
java·开发语言·前端·决策树·c#·ocr·动态规划
用户52203995206517 分钟前
Java多线程并发与加密算法和安全漏洞
java·后端
某鹏17 分钟前
使用jfr让java观测更简单
后端
谷宇18 分钟前
【Java实例-神秘年龄】用Java挑战你的直觉
java·后端
魔镜魔镜_谁是世界上最漂亮的小仙女20 分钟前
JAVA基础【异常处理】
java·后端
真是他22 分钟前
WPF 依赖属性
后端
Cache技术分享22 分钟前
95. Java 数字和字符串 - 操作字符串的其他方法
前端·后端