Skip to content

Yang-xingchen/operator-overloading

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

operator-overloading

实现Java版本的操作符重载功能。以运算符方式使用BigDecimalBigInteger类库。

Important

目前已完成核心功能,后续主要优化使用方式。使用于生产环境请经过测试,欢迎提交issues及PR

使用示例: Main.java

HOW TO USE

默认使用NumberType#BIG_DECIMAL模式,该模式下表达式内的数字转化为java.math.BigDecimal。 支持NumberType#BIG_INTEGER(该模式转成java.math.BigInteger)及NumberType#PRIMITIVE(该模式使用原始类型int, long, double)。

  1. 配置spi(后续考虑移除该步骤),参考见此处:
    1. 添加META-INF/services/javax.annotation.processing.Processor文件,若已存在则忽略该步骤
    2. 在该文件内添加org.yangxc.operatoroverloading.core.processor.MainProcessor(作为单独一行)
  2. 定义服务接口:
    @OperatorService
    public intefact BaseService {}
    
    1. 该类型必须为接口
    2. 必须添加annotation.org.yangxc.operatoroverloading.core.OperatorService注解,具体参数见注释OperatorService.java
    3. 定义方法, 添加annotation.org.yangxc.operatoroverloading.core.ServiceFunction注解,具体参数见注释ServiceFunction.java
    4. 无需实现类
  3. 获取实现
  • SpringBoot: 无需额外操作,自动添加配置类,注册为Spring单例bean MainApplication.java
  • 非SpringBoot: org.yangxc.operatoroverloading.core.Overloading.get(BaseService.class) Main.java

Example1 & Explain

该示例仅作为展示内置的运算功能,如果你仔细分析,会发现无论参数如何该示例返回结果总是相同的,但作为示例已经足够

定义:

@OperatorService(imports = BigDecimal.class)                                                                        // 1
public interface BaseService {                                                                                      // 2

    @ServiceFunction(                                                                                               // 3
            statements = {                                                                                          // 4
                    @Statement(type = BigDecimal.class, varName = "a", exp = "-12.34e2-.56-(.78e-2+(BigDecimal)b)") // 5
            },
            value = "a+(BigDecimal)b"                                                                               // 6
    )
    double test(int b);                                                                                             // 7

}

使用:

BaseService service = Overloading.create(BaseService.class);                                                           // 8
// 结果为: -1234.5678
double res = service.test(1);                                                                                          // 9
  • 2处定义了一个接口
    • 1处声明该接口需要被处理
    • 1处声明导入BigDecimal类,之后可在表达式中使用(BigDecimal)进行强转,如56(BigDecimal)b"是将入参b(原为int型转成BigDecimal进行计算。
    • 1处未定义numberType,默认使用java.math.BigDecimal处理表达式内的数字
    • 1处未定义value,默认使用接口名称+Impl作为实现类的类名,即BaseServiceImpl
  • 7处定义一个方法,入参为int类型的b,在3处定义该方法内容
    • 5处定义一个变量a
      • 类型为BigDecimal
      • 其表达式含义为-12.34e2(实际值为-1234)减.56(实际值为0.56)减(.78e-2(实际值为0.0078)加上(BigDecimal)b(将b转为BigDecimal运算)), 该表达式化简后为1234.5678-b
      • 5处未定义numberType,默认使用方法的numberType处理表达式内的数字,即NumberType#BIG_DECIMAL
    • 6处定义该方法的结果表达式,为本地变量a加上入参b转为BigDecimal的结果
    • 由于7处定义方法的返回类型为double, 定义的操作类型为BigDecimal,故进行转化
    • 3处未定义numberType,默认使用接口的numberType处理表达式内的数字,即NumberType#BIG_DECIMAL
  • 8处获取该类实现对象,在9处调用定义的方法

Tip

表达式中可使用的变量如下:

  1. OperatorClassConst定义的静态变量(内置可用常量见ConstantContext)
  2. ServiceField定义的实例字段
  3. 方法入参
  4. Statement定义的本地变量

上述变量及返回值间的依赖关系需自行处理

Tip

表达式可定义在@ServiceFunctionvalue(表示该方法的返回值)及@Statementexp(表示该变量的运算表达式)

  • 表达式忽略空格、制表符、换行符
  • 可使用上述tip定义的变量,变量类型不支持泛型,需符合操作(即方法入参要求的类型和变量类型需一致,若不一致需要转化,程序不会自动进行转化)
  • 可使用数字,会自动转换为numberType定义的类型,BigDecimalBigInteger将使用表达式内的字符串进行转化
  • 数字定义同Java语法(十进制),支持1(整数), 1_000(下划线分割), 1.23(小数), .23(忽略整数的小数), -.23(负数,需在表达式开头), +.23(正数,需在表达式开头), 1e2(科学计数法,e大小写皆可), 1L(L结尾,L大小写皆可,注意程序将忽略L, 如需定义long,需手动进行转化)
  • 支持+, -, *, /, %运算及()使用子表达式
  • 支持使用(type)expexp转成type类型。如果exp为整个表达式结果,可自动获取返回类型或变量类型进行转化
  • 内置基本类型(除boolean), String, BigDecimal, BigInteger之间互相转化,见ClassOverloading
  • BigDecimalBigInteger转化为基本类型使用xxxValueExact()

该示例编译后生成的代码如下:

public class BaseServiceImpl implements BaseService {

    @Override
    public double test(int b) {
        BigDecimal a = new BigDecimal("-12.34e2").subtract(new BigDecimal("0.56")).subtract(new BigDecimal("0.78e-2").add(new BigDecimal(b)));
        return a.add(new BigDecimal(b)).doubleValue();
    }
    
}

Example2 & Explain

该示例展示自定义重载运算符

实例:

@OperatorClass                                          // 1
public class Complex {                                  // 2
    // 省略字段
    @Cast                                               // 3
    public Complex(Complex2 from) {                     // 4
        // 设置字段
    }
    
    @Cast                                               // 5 
    public Complex2 to() {                              // 6
        Complex2 res;
        // 省略转换操作
        return res;
    }

    @Cast                                               // 7
    public static Complex2 to(Complex3 complex) {       // 8
        Complex2 res;
        // 省略转换操作
        return res;
    }

    @Operator(OperatorType.ADD)                         // 9
    public Complex add(Complex b) {                     // 10
        Complex res;
        // 省略运算操作
        return res;
    }

    @Operator(OperatorType.ADD)                         // 11
    public static Complex add(Complex a, Complex b) {   // 12
        Complex res;
        // 省略运算操作
        return res;
    }
    // 省略其他操作
    // 省略getter、setter、toString等方法
}
  • 2处定义一个实体
    • 添加@OperatorClass注解声明该类注册到程序处理中
  • 4处添加转化构造函数,可使用该构造函数将Complex2转成Complex。该示例中为将(Complex)a编译为new Complex(a)
    • 需要在3处使用@Cast声明该方法用于转换
    • 构造方法参数只能有一个,表示从那个类转成当前类
  • 6处添加转化方法,可使用该构造函数将Complex转成Complex2。该示例中为将(Complex2)a编译为a.to()
    • 需要在5处使用@Cast声明该方法用于转换
    • 方法为实例方法
    • 方法无参数,使用当前类作为要转换的类
    • 方法返回值为转换结果的类
  • 8处添加静态转化方法,可使用该静态转化方法将Complex3转成Complex2。该示例中为将(Complex2)a编译为Complex.to(a)
    • 需要在7处使用@Cast声明该方法用于转换
    • 方法为静态方法
    • 该方法可定义在其他类中(如工具类,处理非该项目的需要操作符重载的类),此时,该类也必须添加@OperatorClass注解
    • 方法参数只能有一个,表示从那个类转换
    • 方法返回值为转换结果的类
  • 10处定义运算方法,可使用该方法进行运算操作。该示例中为将a+b编译为a.add(b)
    • 需要在9处使用@Operator声明方法操作类型
    • 方法为实例方法
    • 方法参数只能有一个,表示操作的第二个对象
    • 方法返回值为计算结果
  • 12处添加静态运算方法,可使用该方法进行运算操作。该示例中为将a+b编译为Complex.add(a, b)
    • 需要在11处使用@Operator声明方法操作类型
    • 方法为静态方法
    • 该方法可定义在其他类中(如工具类,处理非该项目的需要操作符重载的类),此时,该类也必须添加@OperatorClass注解
    • 方法参数必须为2个且相同,表示操作的两个对象
    • 方法返回值为计算结果
  • 运算操作全局只需要一个,如定义多个,程序选择其中之一处理
  • 之后可类似于Example1中使用该类进行运算及转换操作

Tip

为避免非预期情况发生,重载操作符的类最好设计成不可变类。类似于DDD中的ValueObject概念。

Tip

为避免非预期情况发生,操作数的类型及返回类型建议都相同。

Example3 & Explain

该示例展示SpringBoot环境使用

定义:

import org.yangxc.operatoroverloading.core.annotation.spring.Scope;         // 1
// 省略其他导入

@Scope("prototype")                                                         // 2
@OperatorService
public interface TestService {
    // 省略方法
}

使用:

TestService testService = applicationContext.getBean(TestService.class);    // 3
  • 1处导入Scope注意引入的为本框架的注解
  • 2处使用注解,内容同Spring的@Scope注解
  • 3处使用,使用方法同使用Spring bean(包括自动注入、AOP等功能,理论上与使用其他Spring bean没有区别)

Tip

若不添加Scope注解或未配置内容,默认生成的配置中也不添加Scope

Important

框架通过查找包含@SpringBootApplication注解判断是否注入Spring。 若无主类(如提供给其他程序的框架),请添加-AOperatorOverloadingSpringBootConfigName参数(该参数配置@SpringBootConfiguration注解配置类的全限定类名)

该示例编译后生成的代码如下(省略TestService生成实现类):

@SpringBootConfiguration
public class OperatorOverloadingConfiguration {

    @Bean
    @Scope("prototype")
    public TestService testService() {
        return new TestServiceImpl();
    }
    
}

TODO LIST

  • 斜体 为未计划,不一定会实现
  • 删除线 为暂未找到解决方案
  • 文档
  • 支持条件分支
  • 自动化导入(消除spi使用)
  • 支持将科学计数法转成普通数值
  • 支持自定义操作符
  • 支持十六进制、八进制、二进制及转成十进制
  • 支持泛型
  • 内置其他数学类,如: 复数、向量、矩阵
  • 优化表达式,如: 常量折叠
  • 支持变量访问public字段(无法获取字段类型,可通过本地变量+自定义代码(@Statement#parse)实现类似功能)
  • 支持变量调用方法(无法获取返回值类型,可通过本地变量+自定义代码(@Statement#parse)实现类似功能)
  • 支持调用静态方法(无法获取返回值类型,可通过本地变量+自定义代码(@Statement#parse)实现类似功能)
  • ...

About

实现Java版的操作符重载

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages