2012-09-22 29 views
12

Tôi có một lớp đại diện cho một cặp hai giá trị cùng loại (loại mà có thể là bất kỳ một tập hợp cụ thể của các loại):Giới hạn Java Generics hoặc sử dụng sai?

public class Pair<E extends AClass>{ 
     private E var1; 
     private E var2; 
    } 

Lớp này được sử dụng bởi một khuôn khổ, vì vậy nó cần một không constructor -argument trong đó tôi phải nhanh chóng 2 biến (var1, var2):

public class Pair<E extends AClass>{ 
     private E var1; 
     private E var2; 

     public Pair(){ 
      var1 = invoke constructor of type E; 
      var2 = invoke constructor of type E 
     } 
    } 

rõ ràng có một số vấn đề ở đây:

  1. trong để khởi tạo các biến tôi bằng cách nào đó nên biết chính xác kiểu của nó và gọi ra hàm tạo của kiểu cụ thể đó; trong trường hợp tốt nhất điều này có nghĩa có một tuyên bố khá lớn nếu khác trong xây dựng, một cái gì đó như:

    public Pair(){ 
         if(var1 instanceof SpecificType1){ 
          var1 = new SpecificType1(); 
          var2 = new SpecificType2(); 
         } 
        } 
    
  2. Thậm chí nếu tôi làm như trên, tôi sẽ có một số vấn đề vì var1 được khai báo kiểu E và tôi sẽ gặp lỗi không khớp kiểu khi cố gắng khởi tạo SpecficType1 và gán đối tượng kết quả cho var1/var2. Để làm cho nó hoạt động, tôi phải quăng vào E:

    var1 = (E)new SpecificType1(); 
    

Nhưng điều này phá hủy các loại thời gian biên dịch kiểm tra như tôi đang cố gắng để đúc một loại hình cụ thể để một kiểu generic.

Đây có phải là hạn chế của Generics trong java hay là kịch bản này là một trường hợp xấu cho việc sử dụng Generics?

+1

Nếu bạn phải sử dụng 'instanceOf' bên trong thùng chứa chung thì bạn gặp vấn đề về thiết kế. –

+0

Bạn có thể thử [Class.forName()] (http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#forName%28java.lang.String%29) –

+0

Thông báo cho người hỏi (vì chỉnh sửa không thông báo cho người hỏi): Tôi đã thêm một mẫu mã đơn giản cách sử dụng mẫu thiết kế nhà máy trong trường hợp của bạn – amit

Trả lời

6

Để tạo nhanh các biến tôi bằng cách nào đó nên biết chính xác loại của nó và gọi hàm tạo của loại cụ thể đó; trong trường hợp tốt nhất, điều này có nghĩa là có tuyên bố khá lớn nếu có trong constructor, giống như:

Bạn sẽ gặp phải vấn đề trước đó.

if(var1 instanceof SpecificType1){ 
     var1 = new SpecificType1(); 
     var2 = new SpecificType2(); 
    } 

var1null vào thời điểm này, vì vậy var1 instanceof Tfalse cho tất cả T.


Một hạn chế của Java Generics là tham số kiểu generic là erased nên không có cách nào mà bạn có thể suy nghĩ về các tham số kiểu từ một constructor không có đối số.

Người gọi phải cung cấp một số ngữ cảnh để cho bạn biết cách khởi tạo var1var2 và cách điển hình để cung cấp ngữ cảnh đó là thông qua các đối số hàm tạo.


lựa chọn tốt nhất của bạn có lẽ là để cho var1var2 bắt đầu null và sau đó trì hoãn khởi cho đến khi bạn có thể lấy bối cảnh mà bạn cần.

lẽ

void init(Class<E> type) { 
    if (type.isAssignableFrom(ConcreteType1.class)) { 
    var1 = type.cast(new ConcreteType1(...)); 
    var2 = type.cast(new ConcreteType1(...)); 
    } else { /* other branches */ } 
} 

Đây không phải là hoàn hảo kể từ khi bạn vẫn không thể phân biệt E extends List<String> từ E extends List<Number> nhưng nó có thể là đủ tốt cho trường hợp của bạn, và các phương pháp .cast sẽ cung cấp cho bạn một kiểu an cast để E.


Ngoài ra, ổi, Guice, và các thư viện có liên quan cung cấp những thứ như giao diện Supplier<E> có thể có ích trong một phương pháp init.

+0

Vâng, đây cũng là trực giác của tôi. Vì vậy, những hạn chế thậm chí còn nghiêm trọng hơn! – Razvan

+0

Tôi đồng ý với bản chỉnh sửa. Tôi có một hàm tạo khác có 2 biến E là tham số, nhưng khung công tác muốn một hàm tạo không có arg – Razvan

+0

Khuôn khổ sẽ được gọi là init và chắc chắn nó sẽ không làm điều đó. – Razvan

2

Bạn không thể khởi tạo loại chung - Điều gì sẽ xảy ra nếu ví dụ loại chung là SomeAbstractClass? Điều gì sẽ được khởi tạo? (đây không phải là lý do, nó chỉ là trực giác)

Tuy nhiên, bạn có thể sử dụng java reflection API để khởi tạo đối tượng - nhưng bạn sẽ cần đối tượng lớp cụ thể cho đối tượng đó.

Cách thay thế trang nhã hơn là sử dụng abstract factory design pattern và chuyển đối tượng nhà máy cho cặp của bạn và sử dụng đối tượng đó để xây dựng đối tượng cần thiết.


mẫu Mã số:

public class Pair<S> { 
    public final S var1; 
    public final S var2; 
    public Pair(Factory<S> builder) { 
     var1 = builder.build(); 
     var2 = builder.build(); 

    } 
} 

public interface Factory<S> { 
    public S build(); 
} 

public class IntegerBuilder implements Factory<Integer> { 
    private int element = 5; 
    public Integer build() { 
     return new Integer(element++); 
    } 
} 
+0

Một số mã mẫu luôn được chào đón. – OldCurmudgeon

+0

@OldCurmudgeon: Đã làm, có dễ hiểu không? – amit

+0

Tuyệt vời. Và bây giờ cho phương thức 'Lớp', chỉ để hoàn thành. – OldCurmudgeon

1

Nếu một khuôn khổ đã nhanh chóng nó, nó sẽ làm điều đó như một loại nguyên liệu, một cái gì đó tương đương với new Pair() không có tham số kiểu.

Tôi đoán bạn phải tạo lớp lót một đơn giản như:

class SpecificType1Pair extends Pair<SpecificType1> {} 

và vượt qua chúng để khung để thay thế. Bạn có thể nhận thông số loại thực tế là getClass().getGenericSuperclass()).getActualTypeArguments()[0]. Cặp lớp học của bạn sẽ trông giống như sau:

public abstract class Pair<E extends AClass> { 
    private E var1; 
    private E var2; 

    public Pair() { 
     ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass(); 
     @SuppressWarnings("unchecked") 
     Class<E> clazz = (Class<E>) superclass.getActualTypeArguments()[0]; 
     try { 
      var1 = clazz.newInstance(); 
      var2 = clazz.newInstance(); 
     } catch (InstantiationException e) { 
      handle(e); 
     } catch (IllegalAccessException e) { 
      handle(e); 
     } 
    } 
} 
+1

Một '@SuppressWarnings (" unchecked ")' có thể theo thứ tự trên 'Class clazz = (Class ) ...;' line. –

Các vấn đề liên quan