使用泛型类来执行基本的算术运算

时间:2012-12-26 21:00:23

标签: java generics

对于IntegerFloatDouble等包装类型,我想对每个操作使用一个通用方法执行加法,减法,乘法和除法等基本算术运算。不包括BigDecimalBigInteger)。

我尝试使用泛型类做以下内容(用于添加)。

public final class GenericClass<E extends Number> {

    public E add(E x, E y) {
        return x + y; // Compile-time error
    }
}

它发出编译时错误,

  

operator +无法应用于E,E

有没有办法使用这样的通用版本来实现这样的操作?

10 个答案:

答案 0 :(得分:10)

不,没有办法做到这一点,否则它将被内置到Java中。类型系统不够强大,无法表达这类事情。

答案 1 :(得分:6)

不,你不能这样做,因为+运算符不是Number类的一部分。你可以做的是创建一个抽象基类并从中扩展:

static void test() {

    MyInteger my = new MyInteger();
    Integer i = 1, j = 2, k;
    k = my.add(i, j);
    System.out.println(k);
}

static public abstract class GenericClass<E extends Number> {
    public abstract E add(E x, E y);
}

static public class MyInteger extends GenericClass<Integer> {
    @Override
    public Integer add(Integer x, Integer y) {
        return x + y;
    }       
}

(我将这些类设为静态以便于测试,但您可以删除此修饰符。)

您还可以添加一个抽象函数,该函数将获取并返回参数并返回Number类型的值并覆盖它和子类,但返回值所需的强制转换将失去其实用性。

答案 2 :(得分:2)

我能想到的只是将类型作为未知类型接收并使用实例。

类似的东西:

public class GenericClass<? extends Number>{
    public Integer add(? x, ? y){
        if(x instance of Integer && y instance of Integer){
            //Do your math with Integer class methods help
            //Return here
        }
        return (Interger)null;
    }

} 

但不确定:/

答案 3 :(得分:1)

通常你不需要这个,因为使用像double或BigDecimal这样的“超级”类型更简单,更有效,它可以代表任何类型的任何值。

注意:double使用的Integer空间不到{{1}}的一半,并提及它。

答案 4 :(得分:1)

Number是类类型,因此原始类型运算符不适用。此外,Java don't support基元类型中的泛型,因此您将无法绑定E以仅允许基本类型。

解决这个问题的一种方法是在Add方法中单独处理Number支持的每个基本类型,但我认为这会击败你想要完成的任务。

答案 5 :(得分:1)

正如Afonso所建议的,另一种可能性是创建一个函数并记下所有必需的可能性。以下是一些变体:

      // Examples with static functions:
        Integer i = addNumbers (1, 2); // OK
        Double d = addNumbers (3.0, 4.0); // OK
        String s = addObjects ("a", "b"); // OK
//
      // Example with a raw type for a class with both method
      // (or member function) and static functions:
GenericClass gc = new GenericClass();  // Raw type
//
      // Note the error if we don't add a cast:
// i = gc.add(1, 2); // Error: Cannot convert from Number to Integer
//
      // Now OK with a cast but with a Type safety warning:
        i = (Integer) gc.add (1, 2); // Type safety warning.
        i = GenericClass.add2 (1, 2); // OK
        i = GenericClass.add3 (1, 2); // OK
//    
      // Example with an instanciated type for the same class:
        GenericClass<Integer> gc1 = new GenericClass<Integer>();
//
      // Note that we don't need a cast anymore:
        i = gc1.add(1, 2); // Now OK even without casting.
//
        i = GenericClass2.add2 (1, 2); // OK
        s = GenericClass2.add2 ("a", "b"); // OK
        i = GenericClass2.<Integer>add2 (1, 2); // OK.
        d = GenericClass2.<Double>add2 (1.0, 2.0); // OK
        s = GenericClass2.<String>add2 ("a", "b"); // OK
//  
    public static<T extends Number> T addNumbers(T x, T y) {

        if (x instanceof Integer && y instanceof Integer){
            return (T) (Integer) ((Integer)x + (Integer)y);
        } else if (x instanceof Double && y instanceof Double){
            return (T) (Double) ((Double)x + (Double)y);
        } else
            return (T)null;
    }
//    
    public static<T> T addObjects(T x, T y) {

        if (x instanceof Integer && y instanceof Integer) {
      //
      // We must add an (Integer) cast because the type of the operation
      // "((Integer)x + (Integer)y))" is "int" and not "Integer" and we
      // cannot directly convert from "int" to "T".  Same thing for Double
      // but not for the type String:
      //
            return (T) (Integer) ((Integer)x + (Integer)y);
        } else if (x instanceof Double && y instanceof Double) {
            return (T) (Double) ((Double)x + (Double)y);
      } else if (x instanceof String && y instanceof String) {
            return (T) ((String)x + (String)y);            
      } else
            return (T)null;
    }
 //  
    static class GenericClass<T extends Number> {

        public T add(T x, T y) {
            if (x instanceof Integer && y instanceof Integer) {
                return (T) (Integer) ((Integer)x + (Integer)y);
            } else if (x instanceof Double && y instanceof Double) {
                return (T) (Double) ((Double)x + (Double)y);
            } else
                return (T)null;
        }
 //   
        // The type <T> here is NOT the same as the one for the class.
        // We should rename it in order to make this clearer. See add3() 
        // for an example of this.
        public static<T> T add2(T x, T y) {
            if (x instanceof Integer && y instanceof Integer) {
                return (T) (Integer) ((Integer)x + (Integer)y);
            } else if (x instanceof Double && y instanceof Double) {
                return (T) (Double) ((Double)x + (Double)y);
            } else if (x instanceof String && y instanceof String) {
                return (T) ((String)x + (String)y);
            } else
                return (T)null;
        }
 //   
        // The type here is not the same as the one for the class
        // so we have renamed it from <T> to <N> to make it clearer.
        public static<N extends Number> N add3(N x, N y) {
            if (x instanceof Integer && y instanceof Integer) {
                return (N) (Integer) ((Integer)x + (Integer)y);
            } else if (x instanceof Double && y instanceof Double) {
                return (N) (Double) ((Double)x + (Double)y);
            } else
                return (N)null;
        }
    }

答案 6 :(得分:1)

正如Louis Wasserman指出的那样,在Java中无法做到这一点。但是,可以通过使用一些不那么棘手的编程来呈现解决方案。让我们从一个我喜欢的解决方案开始:SylvainL回答这个问题。但是,我相信我们可以退一步处理 Number的每个类型。如果查看Java API,您可以注意到Number的任何子类都必须覆盖几个抽象方法;即intValue()(以及其他人)。使用这些方法,我们可以利用多态性来发挥其真正的潜力。从SylvainL的答案中得到我们的课程,我们可以生成一个新的课程:

public final class EveryNumberClass<E extends Number>
{
    public static int add(E x, E y)
    {
        return x.intValue() + y.intValue();
    }

    public static int subract(E x, E y)
    {
        return x.intValue() - y.intValue();
    }
}

这些操作可以扩展为乘法和除法,虽然不限于Integer s,但可以用来接受任何 Number并表达给定的适当行为一个手术。虽然intValue()方法的行为可能不会返回Number的整数表示,但它肯定会这样做(我查看了most numbers的源代码,包括atomic onesmath ones)。当intValue()返回意外行为时会出现唯一的问题,这可能发生在原子序数,用户定义的Number或大数字被迫缩小的情况下。如果这些是你的项目(或你的图书馆)的问题,我会考虑使用long值,或参考SylvainL的回答。

答案 7 :(得分:0)

希望这可能会帮助那些有兴趣使用Generics进行算术运算的人

  public < T > T add( T a, T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) + Integer.parseInt(b.toString()))));
  }
  public < T > T sub(T a, T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) - Integer.parseInt(b.toString()))));
  }
  public < T> T mul(T a,T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) * Integer.parseInt(b.toString()))));
  }
  public < T  >  T  div (T a, T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) / Integer.parseInt(b.toString()))));
  }
  public < T  > T mod(T a, T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) % Integer.parseInt(b.toString()))));
  }
  public < T  > T leftShift(T a, T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) <<  Integer.parseInt(b.toString()))));
  }
  public < T  > T rightShift(T a, T b)
  {
      return (T) (String.valueOf((Integer.parseInt(a.toString()) >> Integer.parseInt(b.toString()))));
  }

答案 8 :(得分:0)

enum Operator {
    ADD, SUBTRACT, DIVIDE, MULTIPLY;
}

public class GenericArithmeticOperation {

    public GenericArithmeticOperation() {
        // default constructor
    }

    public <E> Integer doArithmeticOperation(E operand1, E operand2, Operator operator) {
        if (operand1 instanceof Integer && operand2 instanceof Integer) {
            switch (operator) {
            case ADD:
                return (Integer) ((Integer) operand1 + (Integer) operand2);
            case SUBTRACT:
                return (Integer) ((Integer) operand1 - (Integer) operand2);
            case MULTIPLY:
                return (Integer) ((Integer) operand1 * (Integer) operand2);
            case DIVIDE:
                return (Integer) ((Integer) operand1 / (Integer) operand2);
            }
        }
        throw new RuntimeException(operator + "is unsupported");
    }

    public static void main(String[] args) {
        GenericArithmeticOperation genericArithmeticOperation = new GenericArithmeticOperation();
        System.out.println(genericArithmeticOperation.doArithmeticOperation(10, 6, Operator.ADD));
        System.out.println(genericArithmeticOperation.doArithmeticOperation(10, 6, Operator.SUBTRACT));
        System.out.println(genericArithmeticOperation.doArithmeticOperation(4, 6, Operator.MULTIPLY));
        System.out.println(genericArithmeticOperation.doArithmeticOperation(21, 5, Operator.DIVIDE));
    }
}

答案 9 :(得分:0)

这可能会对您有所帮助。只需使用您要支持的类型创建不同的操作。而且,您现在可以对泛型使用算术运算。无需昂贵的String解析。

static <V extends Number> V add(V lhs, V rhs)
{
    if (lhs.getClass() == Integer.class && rhs.getClass() == Integer.class)
        return (V) Integer.valueOf(((Integer) lhs) + ((Integer) rhs));
    if (lhs.getClass() == Double.class && rhs.getClass() == Double.class)
        return (V) Double.valueOf(((Double) lhs) + ((Double) rhs));
    if (lhs.getClass() == Float.class && rhs.getClass() == Float.class)
        return (V) Float.valueOf(((Float) lhs) + ((Float) rhs));
    if (lhs.getClass() == Long.class && rhs.getClass() == Long.class)
        return (V) Long.valueOf(((Long) lhs) + ((Long) rhs));
    throw new IllegalArgumentException("unsupported type: " + lhs.getClass());
}

valueOf()将结果包装到Integer对象中,然后可以将其强制转换为V。

要提高性能,请按使用量降序排列。

开关不起作用,因为您必须输入通用或Class<?>,这对于switch语句是不可接受的。