java实现泛型加法
引言:
朴素的数字加法
之前实践中实现数据的加法,很繁琐,比如下面,每一种类型都要写一遍,能不能用泛型方法实现呢?
public Long getSum(Long... args) { long init = 0L; for (Long arg : args) { if (arg == null) { arg = 0L; } init += arg; } return init; } public Integer getSum(Integer... args) { int init = 0; for (Integer arg : args) { if (arg == null) { arg = 0; } init += arg; } return init; }
一、泛型实现-数据类型抽象
借鉴了网上大牛的代码,其实是可以实现的,用了所有数字类的基类Number,把所有类型都当做double类型处理,最后在转成相关类型
虽然牺牲了效率,但更优雅了
public <T extends Number> T getSum(T... args) { Double t = 0.0d; for (T arg : args) { t = t + arg.doubleValue(); } return (T) t; } public <T extends Number> T add(T t1, T t2) { Double all = 0.0d; all = t1.doubleValue() + t2.doubleValue(); return (T) (all); }
二、四则运算-运算符抽象
既然加法可以泛型实现,那么减法、乘除肯定也可以
我们继续抽象一下,把四则运算都实现了
public <T extends Number> T add(T t1, T t2) { Double all = 0.0d; all = t1.doubleValue() + t2.doubleValue(); return (T) (all); } public <T extends Number> T dif(T a, T b) { Double a1 = a == null ? 0.0d : a.doubleValue(); Double b1 = b == null ? 0.0d : b.doubleValue(); Double c1 = a1 - b1; return (T) c1; } public <T extends Number> T mul(T a, T b) { Double a1 = a == null ? 0.0d : a.doubleValue(); Double b1 = b == null ? 0.0d : b.doubleValue(); Double c1 = a1 * b1; return (T) c1; } public <T extends Number> T div(T molecular, T denominator) { Double result; if (denominator == null || denominator.equals(0L)) { result = 0d; } else { result = 100 * molecular.doubleValue() / denominator.doubleValue(); } return (T) result; }
数据类型抽象了,不管有多少不同的number类,我们都可以用一个方法实现。
但是对应加减乘除四则运算,我们还是需要写四个方法,那么运算符能不能抽象?
其实是可以的,我们可以基于java的函数式接口,把运算符也抽象了
public class Test { /** * 针对数字类型的二元操作 * @param t1 * @param t2 * @return * @param <T> */ public static <T extends Number> T binaryOp(T t1, T t2, BiFunction<T,T,T> operator) { T apply = operator.apply(t1, t2); return (T) (apply); } public static void main(String[] args) { Number sum = Test.binaryOp(5, 6, new AddOp()); System.out.println(sum); Number dif = Test.binaryOp(5.0d, 6.0d, new SubOp()); System.out.println(dif); Number product = Test.binaryOp(5l, 6l, new MulOp()); System.out.println(product); Number quo = Test.binaryOp(0x6, 0x3, new DivOp()); System.out.println(quo); } }
现在我们的方法就剩下一个,binaryOp,支持传入两个数字类型,和一个二元操作Function,
当然Function是可以自己定义的,也不仅限于加减乘除。
//加 public class AddOp implements BiFunction<Number, Number, Number> { @Override public Number apply(Number number1, Number number2) { return number1.doubleValue() + number2.doubleValue(); } } //除 public class DivOp implements BiFunction<Number, Number, Number> { @Override public Number apply(Number number1, Number number2) { return number1.doubleValue() / number2.doubleValue(); } } //乘 public class MulOp implements BiFunction<Number, Number, Number> { @Override public Number apply(Number number1, Number number2) { return number1.doubleValue() * number2.doubleValue(); } } //减 public class SubOp implements BiFunction<Number, Number, Number> { @Override public Number apply(Number number1, Number number2) { return number1.doubleValue() - number2.doubleValue(); } }
因为加减乘除是纯函数式编程,我们可以直接定义成单例模式
public class AddOp implements BiFunction<Number, Number, Number> { public static AddOp op = new AddOp(); public static AddOp get() { return op; } private AddOp() { } @Override public Number apply(Number number1, Number number2) { return number1.doubleValue() + number2.doubleValue(); } }
三、参数个数的抽象
我们目前的方法只能支持二元操作,假如我们的数据是可变参数、一个数组或一个集合类型,目前的函数就不够用了。
我们继续抽象一下,把二元操作,抽象为多元操作
首先支持可变参数列表,这样也就支持了数组
/** * 多元操作可支持多个操作对象,需要提供初始值 * 支持可变参数列表和数组 * @param operator * @param initialValue * @param args * @return * @param <T> */ public static <T extends Number> T multivariateOp(BiFunction<T, T, T> operator, T initialValue, T... args) { for (T arg : args) { initialValue = operator.apply(initialValue, arg); } return initialValue; }
但是如果我们的数据来源不是数组,而是来自一个集合或列表,上面的方法就不能用了
我们需要实现一个容器版本的方法,如下
现在所有的容器类,比如Set,List,Queue等实现Iterable的集合都能用了
public static <T extends Number> T multivariateOp(BiFunction<T, T, T> operator, T initialValue, Iterable<? extends T> args) { for (T arg : args) { initialValue = operator.apply(initialValue, arg); } return initialValue; }
四、迭代器版本
可以基于Iterator实现一个版本,这样不管你是可变参数列表,数组,还是集合类型,只要你能转成iterator,就可以使用这个方法。
这也是C++里面的算法常用的形式
public static <T extends Number> T multivariateOp(BiFunction<T, T, T> operator, T initialValue, Iterator<? extends T> args) { while (args.hasNext()) { T arg = args.next(); initialValue = operator.apply(initialValue, arg); } return initialValue; }
五、更一般版本
更多思考...
目前我们的函数已经适用于所有的Number子类,但如果我们要自定义一个复数类Complex,或者一个矩阵类Matrix,显而易见,上述方法是不能用的
因为我们限定了T的范围是Number的子类,那么如何破开这层限制,可以更一般的支持自定义类型呢?
首先我们分析下这个真正的桎梏到底在哪里呢?
我们已经支持了容器类,所以存放元素的容器不是问题了,已经基于函数式接口支持了自定义操作符,所以操作也不是问题
关键还在泛型类型T本身,如果我们让T不再限定为Number的子类,那会怎么样?
我们取消了T的限制,如下
public static <T> T multivariateOp(BiFunction<T, T, T> operator, T initialValue, T... args) { for (T arg : args) { initialValue = operator.apply(initialValue, arg); } return initialValue; }
然后我们自定义一个复数类Complex
public class Complex { int real;//实数 int imag;//虚数 public Complex(int real, int imag) { this.real = real; this.imag = imag; } @Override public String toString() { return "Complex{"+ real + "," + imag + '}'; } }
那么之前的AddOp已经不适用与复数版本,我们需要给复数专门定义操作符了
//复数加法 public class ComplexAddOp implements BiFunction<Complex,Complex,Complex> { //加法原点是(0,0) @Override public Complex apply(Complex complex1, Complex complex2) { return new Complex(complex1.real+complex2.real, complex1.imag+complex2.imag); } } //复数乘法 public class ComplexMulOp implements BiFunction<Complex,Complex,Complex> { //乘法原点是(1,0) @Override public Complex apply(Complex complex1, Complex complex2) { int a = complex1.real;//a=1,b=0 int b = complex1.imag; int c = complex2.real; int d = complex2.imag; return new Complex(a*c-b*d,b*c+a*d); } }
测试一下
public static void main(String[] args) { Complex complex00 = new Complex(1, 0); Complex complex0 = new Complex(0, 0); Complex complex1 = new Complex(1, 2); Complex complex2 = new Complex(3, 4); System.out.println(complex1); System.out.println(complex2); Complex complex = Test.multivariateOp(new ComplexAddOp(), complex0, complex1, complex2); System.out.println(complex); Complex complex3 = Test.multivariateOp(new ComplexMulOp(), complex00, complex1, complex2); System.out.println(complex3); }
输出:
Complex{1,2} Complex{3,4} Complex{4,6} Complex{-5,10}
减法和除法与之相似,就不提供了。
如果定义一个矩阵,只需要定义相关的操作就可以了,或者自定义关系运算用于支持数据库操作,
这样我们的函数算是真正的泛型函数了,支持所有的类型。
todo:通过C++实现矩阵运算、关系运算
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2021-05-31 TLPI读书笔记第63章-备选IO模型2
2021-05-31 netty笔记3-http客户端
2021-05-31 netty笔记2-http服务器