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

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

我尝试使用generics类执行以下(添加)之类的操作。

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

它发出编译时错误,

operator +不能应用于E,E

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

不,没有办法做到这一点,否则它将内置到Java中。 类型系统不足以表达这种事情。

不,你不能这样做,因为+运算符不是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 { public abstract E add(E x, E y); } static public class MyInteger extends GenericClass { @Override public Integer add(Integer x, Integer y) { return x + y; } } 

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

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

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

就像是:

 public class GenericClass{ 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; } } 

但不确定:/

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

注意: double使用不到一半的Integer空间并引用它。

Number是类类型,因此原始类型运算符不适用。 此外,Java中的generics不支持基本类型,因此您将无法绑定E以仅允许基本类型。

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

正如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 gc1 = new GenericClass(); // // 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.add2 (1, 2); // OK. d = GenericClass2.add2 (1.0, 2.0); // OK s = GenericClass2.add2 ("a", "b"); // OK // public static 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 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 { 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  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 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  to  to make it clearer. public static 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; } } 

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

 public final class EveryNumberClass { 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 ,但可以用来接收任何 Number并表示给定操作的适当行为。 虽然intValue()方法的行为可能不会返回Number的整数表示,但它肯定会(我查看了大多数数字的源代码,包括primefaces 数和数学数 )。 当从intValue()返回意外行为时,唯一的问题就会发生,这可能发生在primefaces序数,用户定义的Number s或大数字被迫缩小的情况下。 如果那些是你的项目(或你的图书馆)的问题,我会考虑使用long值,或参考SylvainL的答案。

希望这可能会帮助那些有兴趣使用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())))); } 
 enum Operator { ADD, SUBTRACT, DIVIDE, MULTIPLY; } public class GenericArithmeticOperation { public GenericArithmeticOperation() { // default constructor } public  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)); } }