簡體   English   中英

使用泛型類執行基本算術運算

[英]Using a generic class to perform basic arithmetic operations

對於像IntegerFloatDouble ...(不包括BigDecimalBigInteger )這樣的包裝類型,我想執行基本的算術運算,如加法、減法、乘法和除法,每次操作只使用一種通用方法。

我嘗試使用泛型類執行以下操作(用於添加)。

public final class GenericClass<E extends Number> {

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

它發出編譯時錯誤,

運算符 + 不能應用於 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<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 類型的返回值並覆蓋它和子類,但返回值所需的強制轉換會破壞它的用處。

我所能想到的就是將類型作為未知類型接收並使用實例。

就像是:

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;
    }

} 

但不確定:/

正如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的整數表示形式,但它肯定會返回(我查看了大多數數字的源代碼,包括原子數字和數學數字)。 唯一的問題是從intValue()返回意外行為時,這可能發生在原子序數、用戶定義的Number s 或大數字被迫收縮時。 如果這些是您的項目(或您的庫)的問題,我會考慮使用long值,或參考SylvainL的答案。

通常你不需要這個,因為使用像 double 或 BigDecimal 這樣可以表示任何類型的任何值的“超級”類型更簡單、更有效。

注意: double使用的空間不到Integer的一半,並對其進行引用。

Number是類類型,因此基本類型運算符不適用。 此外,Java 中的泛型不支持原始類型,因此您將無法綁定 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<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;
        }
    }

希望這可以幫助有興趣使用泛型進行算術運算的人

  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 <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));
    }
}

這可能會對您有所幫助。 只需使用您想要支持的類型創建不同的操作。 瞧,你現在可以對泛型使用算術運算了。 無需昂貴的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 不起作用,因為您必須提供泛型或Class<?> ,這對於 switch 語句是不可接受的。

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());
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM