2010-05-02 43 views
10

Tôi đã một loại bộ sưu tập:Làm thế nào để đúc một danh sách các đối tượng kế thừa cho một tập hợp các đối tượng trong Java?

Collection<A> collecA 

Và tôi đã là một danh sách trong đối tượng của tôi:

List<B> listB 

đâu B được mở rộng Một

class B extends A { ... } 

Nhưng tôi không thể làm như sau:

collecA = listB 

Tôi không thể hiểu lý do vì Bộ sưu tập được Danh sách thực hiện.

Trả lời

15

Giả sử một lúc bạn có thể làm những gì bạn mô tả:

class B extends A { ... } 

Collection<A> collecA; 
List<B> listB; 

collecA = listB; // normally an error, but lets pretend its allowed 

collecA.add(new A()); // PROBLEM! 

Phương pháp gọi collecA.add(new A()) vẻ ổn kể từ collecA là một bộ sưu tập chứa A s. Tuy nhiên, nếu việc gán ở trên được cho phép, thì chúng tôi gặp vấn đề vì collecA thực sự tham chiếu đến cá thể List<B> - Tôi chỉ thêm A vào danh sách chỉ có thể giữ B s!

Người hỏi cũng cho biết:

Tôi không thể hiểu tại sao từ Bộ sưu tập được thực hiện bởi danh sách.

Không quan trọng Bộ sưu tập là siêu lớp của Danh sách. Bài tập này là bất hợp pháp ngay cả khi bạn sử dụng hai danh sách.

class B extends A { ... } 
List<A> listA; 
List<B> listB; 
listA = listB; // still an error, still leads to the same problem 

Điều quan trọng là các List<A>biến có thể tham khảo chỉ List s có thể chứa A s. Tuy nhiên, một ví dụ List<B> không được giữ A s. Do đó, một biến số List<A> như listA không thể được chỉ định tham chiếu đến List<B>phiên bản được gọi bằng listB.

Hoặc nói tổng quát hơn: B là một lớp con của A không không ngụ ý rằng SomeGenericClass<B> là một lớp con của SomeGenericClass<A> (JLS §4.10: subtyping không mở rộng thông qua các loại generic: T <: U không có nghĩa là C<T> <: C<U>.)


Đó là ví dụ này/loại suy từ Java Generics Tutorial đã giúp tôi hiểu điều này:

http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html

"Hiểu tại sao trở nên dễ dàng hơn nhiều nếu bạn nghĩ về đối tượng hữu hình - những thứ bạn có thể thực sự hình ảnh - ví dụ như một cái lồng:

// A cage is a collection of things, with bars to keep them in. 
interface Cage<E> extends Collection<E>; 
... 
Cage<Lion> lionCage = ...; 
Cage<Butterfly> butterflyCage = ...; 

Nhưng những gì về một "lồng động vật"? Anh là mơ hồ, vì vậy để được chính xác chúng ta hãy giả sử chúng ta đang nói về một "lồng tất cả động vật" :

Cage<Animal> animalCage = ...; 

Đây là một cái lồng được thiết kế để giữ tất cả các loại động vật, pha trộn với nhau. Nó phải có các thanh đủ mạnh để giữ trong sư tử, và khoảng cách đủ gần để giữ trong những con bướm.
...
Vì sư tử là một loại động vật (Sư tử là một loại động vật), câu hỏi sau đó sẽ trở thành "Lồng sư tử là một loại lồng động vật? Cage<Lion> là loại phụ của Cage<Animal>?". Theo định nghĩa trên của lồng động vật, câu trả lời phải là "không". Điều này thật đáng ngạc nhiên! Nhưng nó làm cho cảm giác hoàn hảo khi bạn nghĩ về nó: Một cái lồng sư tử không thể được giả định để giữ trong bướm, và một cái lồng bướm không thể được giả định để giữ trong sư tử. Do đó, không phải lồng có thể được coi là một "tất cả động vật" lồng:..

animalCage = lionCage; // compile-time error 
animalCage = butterflyCage; // compile-time error 

"

+3

Tốt dễ hiểu giải thích Bert Tôi luôn đánh giá cao ngớ ngẩn suy khi cố gắng tìm hiểu Generics và thừa kế Kartoch nên – tgai

+2

@Tamon - Rất vui khi bạn (và hy vọng những người khác) thấy dễ hiểu. Tôi đã có cùng một vấn đề hiểu biết về generics và thừa kế (chưa đề cập đến các ký tự chung), vì vậy nó lấy một ví dụ mới như trong Java Generics Tutorial để lấy nó trong đầu của tôi.Tôi lấy một tìm kiếm cho 'java generic cast animal cage' để nhớ chính xác nơi tôi đọc tương tự này. :-) –

+0

Rất tốt lời giải thích thực sự ... – Kartoch

5

Generics Java không phải là covariant.

Xem Java Theory and Practice: Generics Gotchas để biết thêm chi tiết.

Trang cho thấy một ví dụ đơn giản đó sẽ tàn phá các kiểu hệ thống nếu đó là hiệp biến:

Hãy tưởng tượng bạn có thể gán một Integer Danh sách <> để một số Danh sách <>. Sau đó, các mã sau đây sẽ cho phép bạn đặt một cái gì đó không phải là một Integer vào một danh sách < Integer>:

List<Integer> li = new ArrayList<Integer>(); 
List<Number> ln = li; // illegal 
ln.add(new Float(3.1415)); // ERROR: Adds a float to li, which is a list of Integers! 
8
Collection<? extends A> collecA 

này sửa chữa nó. Vấn đề không phải là List extends Collection, nhưng thay vào đó là các loại chung chung.

1

Bạn có thể gán Danh sách < B> vào Bộ sưu tập < B>, nhưng không liệt kê < B> để Bộ sưu tập < A>.

Hãy tưởng tượng điều gì sẽ xảy ra nếu điều này là có thể:

List<B> = new ArrayList<B>(); 
Collection<A> collecA = listB; //Assume that this line compiles 
collecA.add(new A()); 
B item = listB.get(0); //ClassCastException! 

Như bạn thấy, chúng ta "lừa" hệ thống Generics loại, bằng cách thêm một thể hiện của bê tông loại A đến một bộ sưu tập mà được cho là chỉ có đối tượng thuộc loại B (hoặc con cháu). Kết quả là, dòng cuối cùng thực hiện một phép diễn xuất ngầm đến B không thành công với một ClassCastException. Có gì sai với nó? Trình biên dịch không thể đảm bảo an toàn loại, và đó là chống lại một trong các nguyên tắc Generics Java.

Do đó, đã được quyết định rằng Danh sách < B> là Bộ sưu tập < B>, nhưng KHÔNG Liệt kê < A> (hoặc Bộ sưu tập < A>).

Là một nhận xét phụ, điều thú vị là lưu ý rằng các mảng không tuân theo cùng một quy tắc: Chuỗi [] một đối tượng [] và các nhiệm vụ là hợp pháp.

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