实现Java版本的操作符重载功能。以运算符方式使用BigDecimal及BigInteger类库。
Important
目前已完成核心功能,后续主要优化使用方式。使用于生产环境请经过测试,欢迎提交issues及PR
使用示例: Main.java
默认使用NumberType#BIG_DECIMAL模式,该模式下表达式内的数字转化为java.math.BigDecimal。
支持NumberType#BIG_INTEGER(该模式转成java.math.BigInteger)及NumberType#PRIMITIVE(该模式使用原始类型int, long, double)。
- 配置spi(后续考虑移除该步骤),参考见此处:
- 添加
META-INF/services/javax.annotation.processing.Processor文件,若已存在则忽略该步骤 - 在该文件内添加
org.yangxc.operatoroverloading.core.processor.MainProcessor(作为单独一行)
- 添加
- 定义服务接口:
@OperatorService public intefact BaseService {}- 该类型必须为接口
- 必须添加
annotation.org.yangxc.operatoroverloading.core.OperatorService注解,具体参数见注释OperatorService.java - 定义方法, 添加
annotation.org.yangxc.operatoroverloading.core.ServiceFunction注解,具体参数见注释ServiceFunction.java - 无需实现类
- 获取实现
- SpringBoot: 无需额外操作,自动添加配置类,注册为Spring单例bean MainApplication.java
- 非SpringBoot:
org.yangxc.operatoroverloading.core.Overloading.get(BaseService.class)Main.java
该示例仅作为展示内置的运算功能,如果你仔细分析,会发现无论参数如何该示例返回结果总是相同的,但作为示例已经足够
定义:
@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)进行强转,如5和6中(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
表达式中可使用的变量如下:
- 由OperatorClassConst定义的静态变量(内置可用常量见ConstantContext)
- 由ServiceField定义的实例字段
- 方法入参
- 由Statement定义的本地变量
上述变量及返回值间的依赖关系需自行处理
Tip
表达式可定义在@ServiceFunction的value(表示该方法的返回值)及@Statement的exp(表示该变量的运算表达式)
- 表达式忽略空格、制表符、换行符
- 可使用上述tip定义的变量,变量类型不支持泛型,需符合操作(即方法入参要求的类型和变量类型需一致,若不一致需要转化,程序不会自动进行转化)
- 可使用数字,会自动转换为
numberType定义的类型,BigDecimal及BigInteger将使用表达式内的字符串进行转化 - 数字定义同Java语法(十进制),支持
1(整数),1_000(下划线分割),1.23(小数),.23(忽略整数的小数),-.23(负数,需在表达式开头),+.23(正数,需在表达式开头),1e2(科学计数法,e大小写皆可),1L(L结尾,L大小写皆可,注意程序将忽略L, 如需定义long,需手动进行转化) - 支持
+,-,*,/,%运算及()使用子表达式 - 支持使用
(type)exp将exp转成type类型。如果exp为整个表达式结果,可自动获取返回类型或变量类型进行转化 - 内置基本类型(除
boolean),String,BigDecimal,BigInteger之间互相转化,见ClassOverloading BigDecimal及BigInteger转化为基本类型使用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();
}
}该示例展示自定义重载运算符
实例:
@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
为避免非预期情况发生,操作数的类型及返回类型建议都相同。
该示例展示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();
}
}
- 斜体 为未计划,不一定会实现
删除线为暂未找到解决方案
- 文档
- 支持条件分支
- 自动化导入(消除
spi使用) - 支持将科学计数法转成普通数值
- 支持自定义操作符
- 支持十六进制、八进制、二进制及转成十进制
- 支持泛型
- 内置其他数学类,如: 复数、向量、矩阵
- 优化表达式,如: 常量折叠
-
支持变量访问(无法获取字段类型,可通过本地变量+自定义代码(public字段@Statement#parse)实现类似功能) -
支持变量调用方法(无法获取返回值类型,可通过本地变量+自定义代码(@Statement#parse)实现类似功能) -
支持调用静态方法(无法获取返回值类型,可通过本地变量+自定义代码(@Statement#parse)实现类似功能) - ...