2012-10-14 40 views
7

Tôi có một vùng chứa (Danh sách) của một số thành phần thuộc loại T và muốn lọc nó. Vì vậy, nó chỉ chứa các phần tử của một loại phụ cụ thể U. Có thể đặt loại trả về "động" không?Loại trả về động Java?

dụ:

class SomeContainer<T> extends ArrayList<T>{ 

    public SomeContainer<T> subset(Class c){ 
     SomeContainer<...here the type of c > output = new SomeContainer<.. also ..>(); 

     //filter own elements and only add c-objects in the new list 

     return output; 
    } 
} 

Tại thời điểm này nó sẽ trả về một danh sách các loại T chung chung và không thuộc về c-Class-type (Loại con của sự T). Vì vậy, tôi đôi khi nhận được biên dịch-Thông báo sau:

Note: SomeContainer.java uses unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 

Bởi vì tôi muốn lọc danh sách sau khi đối tượng của một subtype và kích hoạt các phương pháp kiểu phụ cụ thể tôi sẽ cần một danh sách kiểu phụ cụ thể.

+2

Cảnh báo là vì bạn đang sử dụng 'Lớp' thay vì' Lớp '. Và tôi không chắc chắn rằng bạn có thể sử dụng các loại động trong các tham số mẫu. – vainolo

+0

* Vì vậy, nó chỉ chứa các phần tử của một kiểu phụ cụ thể U * Tại sao không khai báo với kiểu phụ cụ thể đó? – m0skit0

Trả lời

9

java.lang.Class là một kiểu generic tham số trên chính nó, vì vậy bạn có thể sử dụng tham số kiểu của nó, như thế này:

public <U extends T> SomeContainer<U> subset(Class<U> c){ 
    SomeContainer<U> output = new SomeContainer<U>(); 
    for (T val : this) { 
     if (c.isInstance(val)) { 
      output.add(c.cast(val)); 
     } 
    } 
    return output; 
} 
+3

Hoặc tốt hơn, 'output.add (c.cast (val))' để tránh cảnh báo truyền được bỏ chọn. –

+0

@IanRoberts Tuyệt đối! Cảm ơn vì một gợi ý tuyệt vời! – dasblinkenlight

1

Generics là chỉ một vật phẩm thời gian biên dịch, do đó chương trình của bạn không thể hoạt động được. Trình biên dịch không thể dự đoán lớp nào bạn sẽ muốn ở mỗi lần thực thi dòng mã thực hiện cuộc gọi đến hàm này. Bạn không thể làm cho giải pháp này an toàn loại trừ khi bạn có một trường hợp rất hạn chế và hoàn toàn vô dụng khi bạn chỉ sử dụng các chữ cái lớp để gọi hàm của bạn. Điều đó sẽ, tuy nhiên gần như chắc chắn đánh bại mục đích của nó được, như bạn đã nói, năng động.

0

Điểm đầu tiên: bạn có thể xóa cảnh báo hoạt động không được chọn bằng cách thay thế đối số Class c bằng Class<T> c. Đó có thể là tất cả những gì bạn cần ... trong trường hợp này ... = :-)

Điểm thứ hai: Thông thường mã gọi là SomeContainer.subset() sẽ biết tại loại biên dịch loại U (từ ngữ cảnh logic) . Đây phải là trường hợp của bạn, nếu không bạn sẽ không thể vượt qua Class c luận trong

Hãy thử:.

class SomeContainer<T> extends ArrayList<T>{ 

    public <U extends T> SomeContainer subset(Class<U> c){ 
     SomeContainer<U> output = new SomeContainer<U>(); 
     // put filtered elements into output 

     return output;  
    } 
} 

Xem những gì tôi đã có?
Giới thiệu tham số loại thứ hai trên cuộc gọi phương thức: U extends T. Cũng sử dụng điều này trong đối số với Class<U> c.
Người gọi sẽ gọi tương tự (trong đó X được chọn lớp con của T):

SomeContainer<X> mySubset = mySomeContainer.subset(X.class);  // type inference 
SomeContainer<X> mySubset = mySomeContainer.<X>subset(X.class); // type arg specified 


Nếu bạn cần một cái gì đó nhiều hơn năng động hơn thế này, ký tự đại diện có thể hỗ trợ - cho phép một "gia đình" của parametised loại để được thông qua trong & ra:

public SomeContainer<? extends X> subset(Class<? extends X> c){ 

This is a "nhựa" giao diện chức năng: bạn có thể trở SomeContainer<T> hoặc 012.cho bất kỳ X mà là một lớp con của T. Sau đây cũng làm việc:

public SomeContainer<? super Z> subset(Class<? extends X> c){ 

Tuy nhiên, như một poster đã nói, Generics là một cấu trúc thời gian biên dịch, chúng được thay thế trong quá trình biên dịch với tạo mã không chung chung. Điều này có nghĩa là bạn không thể tự động quyết định loại được sử dụng để khởi tạo một loại chung với một dòng mã.Nhưng bạn có thể gian lận một chút: Nếu bạn có một số lượng giới hạn các lớp con của T, nói X, Y và Z, trong đó Z mở rộng Y, và Y mở rộng Z, thì bạn có thể sử dụng một hacky "if statement" tốt. Hãy thử:

lớp SomeContainer kéo dài ArrayList {

public SomeContainer<? extends X> subset(Class<? extends X> c){ 
    SomeContainer<? extends X> output = null; 

    // would like to use: "if (c instance of Class<Z>)" 
    // but instanceof does not allow generic type arguments 
    if (c.getName().equals(Z.class.getName())) { 
     SomeContainer<Z> outputZ = new SomeContainer<Z>(); 
     // put filtered elements into outputZ 
     output = outputZ; 
    } else if (c.getName().equals(Y.class.getName())) { 
     SomeContainer<Y> outputY = new SomeContainer<Y>(); 
     // put filtered elements into outputZ 
     output = outputY; 
    } else if (c.getName().equals(X.class.getName())) { 
     SomeContainer<X> outputX = new SomeContainer<X>(); 
     // put filtered elements into outputZ 
     output = outputX; 
    } 
    return output;  
} 

}

dễ dàng! (hoặc không) = :-)

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