[英]Using a generic class to perform basic arithmetic operations
對於像Integer
、 Float
、 Double
...(不包括BigDecimal
和BigInteger
)這樣的包裝類型,我想執行基本的算術運算,如加法、減法、乘法和除法,每次操作只使用一種通用方法。
我嘗試使用泛型類執行以下操作(用於添加)。
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
的一半,並對其進行引用。
正如 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.