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++实现矩阵运算、关系运算

posted @ 2023-05-31 10:01  Mars.wang  阅读(411)  评论(0编辑  收藏  举报