12

我想执行基本的算术运算,如加法、减法、乘法和除法,每次操作只使用一种通用方法,用于包装类型,如Integer, Float, Double...(不包括BigDecimaland BigInteger)。

我尝试使用泛型类执行以下操作(用于添加)。

public final class GenericClass<E extends Number> {

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

它发出编译时错误,

运算符 + 不能应用于 E,E

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

4

11 回答 11

13

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

于 2012-12-26T21:00:50.770 回答
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 类型的参数和返回值,并覆盖它和子类,但返回值所需的强制转换会破坏它的用处。

于 2012-12-26T22:31:02.550 回答
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;
    }

} 

但不确定:/

于 2012-12-26T23:27:19.843 回答
1

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

注意: adouble使用了不到一半的空间来Integer引用它。

于 2012-12-26T21:05:52.550 回答
1

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

解决此问题的一种方法是在 Add 方法中分别处理 Number 支持的每种原始类型,但我认为这会破坏您要完成的工作。

于 2012-12-26T21:08:06.270 回答
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;
        }
    }
于 2012-12-27T15:26:31.990 回答
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();
    }
}

这些运算可以扩展为乘法和除法,虽然不限于Integers,但可以用于接受任何 Number运算并表达给定运算的适当行为。虽然该intValue()方法的行为可能不会返回 的整数表示Number,但它肯定会返回(我查看了大多数数字的源代码,包括原子数字和数学数字)。唯一的问题是从 中返回意外行为intValue(),这可能发生在原子序数、用户定义Number的 s 或大数字被迫收缩时。如果这些是您的项目(或您的图书馆)的问题,我会考虑使用long值,或参考SylvainL的答案。

于 2016-06-23T19:11:18.947 回答
0

希望这可以帮助有兴趣使用泛型进行算术运算的人

  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()))));
  }
于 2017-01-10T04:47:09.590 回答
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));
    }
}
于 2017-01-18T02:44:10.437 回答
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.

为了提高性能,请按使用量降序更改顺序。

switch 不起作用,因为您必须提供泛型或 a Class<?>,这对于 switch 语句是不可接受的。

于 2019-11-01T20:37:37.507 回答
0
public class GenericsDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        MathC<Number> obj1 = new MathC<>();
        System.out.println(obj1.add(2,3.4));
        System.out.println(obj1.add(2.3,3.4));
        
    }

}

class MathC<.T extends Number>{            //remove . before T
    
    
    public Double add(T i , T j) {
        return (i.doubleValue()+j.doubleValue());
    }
}
于 2021-12-31T07:17:31.150 回答