2009-04-14 60 views
45

Tôi muốn tạo một mảng các Lớp, mỗi lớp đại diện cho một loại có sẵn trong hệ thống mà tôi đang xây dựng. Tất cả các lớp liên quan là các lớp con của một siêu lớp chung. Vì vậy, tôi muốn làm:Làm cách nào để sử dụng Generics với một loạt các Lớp học?

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class }; 

này mang lại cho tôi những lỗi:

Cannot create a generic array of Class<? extends SuperClass>. 

Tôi nhận được thông báo tương tự nếu tôi cố gắng để đủ điều kiện tạo ra các mảng ở phía bên tay phải của khởi tạo:

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class }; 

tôi có thể lấy mã để biên dịch nếu tôi loại bỏ Generics trình độ:

Class[] availableTypes = { SubClass1.class, SubClass2.class }; 

Nhưng sau đó tôi nhận được cảnh báo generics:

Lớp là loại thô. Tham chiếu đến kiểu generic class nên được tham số hóa.

Tôi đang cố gắng; Tôi đang cô! :) Ngoài ra, tại thời điểm này, ngay cả khi điều này đã không kích động một cảnh báo, tôi bị mất một phần của giao diện tôi đã cố gắng xác định. Tôi không muốn trả lại một mảng các lớp tùy ý; Tôi muốn trả về một mảng các lớp là tất cả các lớp con của một SuperClass cụ thể!

Eclipse có một số công cụ khá mạnh mẽ để tìm ra tham số nào để sử dụng để sửa chữa các khai báo Generics, nhưng trong trường hợp này nó rơi xuống, vì nó có xu hướng làm khi bạn xử lý Class. Các "Infer Generic Type Arguments" thủ tục nó cung cấp không thay đổi mã ở tất cả, để lại cảnh báo.

tôi đã có thể làm việc này bằng cách sử dụng một Bộ sưu tập thay vì:

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>(); 

Nhưng đúng cách để làm điều này với mảng là gì?

Trả lời

22

Có vẻ như có một chút thất bại, nhưng các vấn đề như thế này chính xác là lý do tại sao hầu hết mọi người tránh trộn lẫn các mảng và generics. Do cách thức generics được thực hiện (type erasure), mảng và generics sẽ không bao giờ làm việc tốt với nhau.

Hai cách giải quyết:

  • Stick với việc sử dụng một bộ sưu tập (ví dụ ArrayList<Class<? extends SuperClass>>), trong đó hoạt động giống cũng như một mảng và cũng cho phép mở rộng.
  • Đặt chú thích @SuppressWarnings("unchecked") trên mã tạo mảng cùng với nhận xét cho thấy việc sử dụng nó.
2

Vấn đề là tạo ra một mảng chung loại là bất hợp pháp. Cách duy nhất để vượt qua nó là bằng cách chuyển sang kiểu chung khi bạn tạo mảng, nhưng đó không phải là giải pháp tốt. (Lưu ý rằng nó có thể sử dụng một mảng chung, chỉ cần không tạo ra một:. Thấy this question)

Bạn nên hầu như luôn sử dụng danh sách thay vì mảng dù sao, vì vậy tôi nghĩ rằng bạn đã đưa ra giải pháp tốt nhất đã .

14

Sử dụng cú pháp sau:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... }; 

Nó sẽ cung cấp cho bạn một "kiểm soát" cảnh báo, và đúng như vậy, vì bạn có thể bao gồm một đối tượng Class cho một loại mà không mở rộng SuperClass trong mảng.

11

Cách đúng để thực hiện việc này với mảng là thực hiện việc đó với Bộ sưu tập. Lấy làm tiếc! Đối với một loạt các lý do phức tạp, mảng không chơi tốt đẹp với Generics. Các mảng có một mô hình hiệp phương sai khác so với các đối tượng chung làm, điều này cuối cùng gây ra các vấn đề bạn đang gặp phải. Ví dụ, với mảng, nhưng không (thường) với các đối tượng chung chung, bạn có thể hợp pháp làm việc này:

Object[] myArray = new String[5]; 

trong khi bạn không thể làm điều này:

LinkedList<Object> myCollection = new LinkedList<String>(); 

Nếu bạn muốn biết thêm chi tiết, bạn có thể nhìn thấy trang Arrays In Java Generics từ số xuất sắc Generics FAQ

Như simonn đã nói, bạn cũng có thể sử dụng mảng của mình như cũ, và sử dụng @SuppressWarnings("unchecked") để tắt tiếng cảnh báo. Điều này sẽ hoạt động, nhưng không có sự an toàn loại mà generics có thể cung cấp cho bạn. Nếu đó là hiệu suất mà bạn đang lo lắng về, chỉ cần sử dụng một ArrayList vì vậy bạn chỉ sử dụng một wrapper mỏng xung quanh một mảng, nhưng với tất cả các loại bảo đảm an toàn được cung cấp bởi generics.

+1

Hiệp phương sai! Bạn nói từ xấu! –

4

But what's the right way to do this with arrays?

Không có cách an toàn nào để thực hiện việc này; sử dụng bộ sưu tập là phương pháp phù hợp. Để xem tại sao, hãy tưởng tượng nếu điều này được cho phép. Bạn có thể có một tình huống như thế này:

// Illegal! 
Object[] baskets = new FruitBasket<? extends Citrus>[10]; 

// This is okay. 
baskets[0] = new FruitBasket<Lemon>(); 

// Danger! This should fail, but the type system will let it through. 
baskets[0] = new FruitBasket<Potato>(); 

Hệ thống loại cần để phát hiện xem một giỏ mà được thêm vào mảng là loại FruitBasket<? extends Citrus> hoặc một subtype. Một FruitBasket không phù hợp và nên bị từ chối với một số ArrayStoreException. Nhưng không có gì xảy ra!

Vì loại xóa, JVM chỉ có thể xem loại thời gian chạy của mảng. Khi chạy, chúng ta cần so sánh kiểu mảng với kiểu phần tử để đảm bảo chúng khớp nhau. Loại thành phần thời gian chạy của mảng là FruitBasket[] sau khi xóa loại; tương tự, loại thời gian chạy của phần tử là FruitBasket. Không có vấn đề gì sẽ được phát hiện - và đó là lý do tại sao điều này là nguy hiểm.

0

Không gõ an toàn và với cảnh báo dàn diễn viên không được kiểm soát:

Class<? extends SuperClass>[] availableTypes = 
    (Class<? extends SuperClass>[]) 
    (new Class[]{ SubClass1.class, SubClass2.class }); 
Các vấn đề liên quan