2012-12-26 38 views
6

Tôi muốn thực hiện phép tính số học cơ bản như cộng, trừ, nhân và chia chỉ sử dụng một phương pháp chung cho mỗi hoạt động với nhiều loại wrapper như Integer, Float, Double ... (trừ BigDecimalBigInteger).Sử dụng một lớp tổng quát để thực hiện phép tính số học cơ bản

Tôi đã cố gắng thực hiện một việc như sau (để thêm) bằng cách sử dụng một lớp chung.

public final class GenericClass<E extends Number> { 

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

Nó đưa ra một lỗi thời gian biên dịch,

operator + không thể được áp dụng cho E, E

Có cách nào để sử dụng một phiên bản chung chung như vậy để đạt được các hoạt động đó ?

+1

này nghe có vẻ như overengineering lĩnh vực vấn đề. – Woot4Moo

Trả lời

8

Không, không có cách nào để thực hiện việc này, nếu không nó sẽ được tích hợp vào Java. Hệ thống kiểu không đủ mạnh để diễn tả điều này.

1

Nói chung bạn không cần điều này vì nó đơn giản và hiệu quả hơn để sử dụng loại "siêu" như double hoặc BigDecimal có thể đại diện cho bất kỳ giá trị nào thuộc loại nào.

Lưu ý: double sử dụng ít hơn một nửa không gian của Integer có tham chiếu đến nó.

1

Number là loại lớp, do đó các toán tử kiểu nguyên thủy không áp dụng. Ngoài ra, các generic trong Java don't support các kiểu nguyên thủy, do đó bạn sẽ không thể kết buộc E để chỉ cho phép các kiểu nguyên thủy.

Một cách xung quanh việc này là xử lý từng loại nguyên thủy mà Số hỗ trợ riêng trong phương thức Thêm, nhưng tôi nghĩ rằng sẽ đánh bại những gì bạn đang cố gắng hoàn thành.

6

Không, bạn không thể làm điều đó vì toán tử + không thuộc lớp Số. Những gì bạn có thể làm là để tạo ra một lớp cơ sở trừu tượng và kéo dài từ nó: (. Tôi đã thực hiện các lớp tĩnh để thuận lợi cho việc thử nghiệm nhưng bạn có thể xóa sửa đổi lần này)

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

Bạn cũng có thể thêm một hàm trừu tượng sẽ lấy và trả về các tham số và giá trị trả về của kiểu Number và ghi đè nó và các lớp con nhưng việc truyền yêu cầu cho giá trị trả về sẽ đánh bại tính hữu ích của nó.

+0

Về kịch bản trong câu hỏi, không có nghĩa là sử dụng một lớp chung theo cách này. Ngoài ra, việc khai báo một lớp tĩnh theo cách này không được cho phép trong Java (tuy nhiên đây là trường hợp với C#). Một lớp tĩnh phải có ít nhất một lớp không có ** tĩnh ** kèm theo (các lớp ngoài cùng không thể là tĩnh), tức là một lớp tĩnh phải được chứa bởi ít nhất một lớp không có tĩnh ngoài cùng. – Lion

+0

Đối với các lớp tĩnh, tôi không nói rằng tôi không kèm theo chúng vào lớp khác. Tôi đã thực hiện các tĩnh đơn giản chỉ vì tôi đã instanciating chúng từ một chức năng tĩnh nhưng bạn nói đúng, nó sẽ có được đơn giản hơn để chỉ cần loại bỏ từ này ở khắp mọi nơi. Như câu lệnh khác của bạn, "Về kịch bản trong câu hỏi, không có ý nghĩa gì khi sử dụng một lớp chung theo cách này", tôi không hoàn toàn chắc chắn để hiểu bạn đang nói gì nhưng cho phép tôi nói điều đó đơn giản là không thể trong Java để giải quyết kịch bản gốc chính xác như được mô tả. – SylvainL

2

Tất cả những gì tôi có thể nghĩ là đang nhận loại dưới dạng loại không xác định và sử dụng phiên bản.

Cái gì như:

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

} 

Nhưng không chắc chắn:/

1

Theo đề nghị của Afonso, một khả năng khác sẽ được tạo một hàm và viết xuống tất cả các khả năng cần thiết. Dưới đây là một số biến thể:

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

Louis Wasserman chỉ ra, không có cách nào để thực hiện điều này trong Java. Tuy nhiên, một giải pháp có thể được trình bày bằng cách sử dụng một số chương trình không khó khăn.Hãy bắt đầu với một giải pháp mà tôi thích: SylvainL câu trả lời cho câu hỏi. Tuy nhiên, tôi tin rằng chúng tôi có thể quay lại một bước và xử lý mọi loại của Number. Nếu bạn nhìn vào Java API, bạn có thể lưu ý rằng bất kỳ lớp con nào của Number đều phải ghi đè lên một vài phương pháp trừu tượng; cụ thể là, intValue() (cũng như những người khác). Sử dụng các phương pháp này, chúng ta có thể sử dụng đa hình cho tiềm năng thực sự của nó. Taken lớp chúng tôi có từ câu trả lời SylvainL 's, chúng ta có thể tạo ra một lớp mới như vậy:

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

Các hoạt động này có thể được mở rộng để nhân và chia, và trong khi không giới hạn Integer s, có thể được sử dụng để lấy trong bất kỳNumber và thể hiện hành vi thích hợp cho một hoạt động. Và trong khi hành vi của phương pháp intValue()có thể không trả về một số nguyên đại diện cho Number, thì chắc chắn nhất (tôi đã xem mã nguồn cho most numbers, bao gồm atomic onesmath ones). Vấn đề duy nhất sẽ xảy ra khi hành vi không mong muốn được trả lại từ intValue(), có thể xảy ra với số nguyên tử, người dùng được xác định Number s hoặc khi số lớn bị buộc phải thu nhỏ. Nếu đó là những lo ngại cho dự án của bạn (hoặc cho thư viện của bạn), tôi sẽ xem xét sử dụng các giá trị long hoặc đề cập đến câu trả lời của SylvainL.

0

Hy vọng điều này có thể giúp đỡ một ai đó là quan tâm để làm việc về hoạt động số học sử dụng 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())))); 
    } 
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)); 
    } 
} 
Các vấn đề liên quan