2011-12-23 17 views
7

Tôi không hiểu tại sao những điều sau không hiệu quả và tôi chắc chắn câu trả lời có liên quan đến một điều cơ bản mà tôi không hiểu và hy vọng ai đó có thể trợ giúp.Vấn đề với các loại chung của ArrayList và thừa kế giao diện

Tôi hiểu về việc sử dụng giao diện trong một ArrayList ví dụ rằng nếu tôi có:

public interface Weapon { ... } 
public class Gun implements Weapon { ...} 
public class Knife implements Weapon { ... } 

bạn có thể sau đó chèn bất cứ điều gì mà thực hiện vũ khí vào mảng một vũ khí:

ArrayList<Weapon> weapons = new ArrayList<Weapon>(); 
weapons.add(new Gun()); 
weapons.add(new Knife(); 

Tôi nhận được rằng , nhưng điều gây nhầm lẫn cho tôi là sự hiểu biết tại sao ArrayList<Gun> không tương thích với ArrayList<Weapon> theo những cách khác. Để minh họa, sau đây là hợp pháp:

public void interfaceIsTheArgument(Weapon w) { ... } 
... 
interfaceIsTheArgument(new Gun()); 
interfaceIsTheArgument(new Knife()); 

nhưng đây không phải là:

public void interfaceIsTheArgument(ArrayList<Weapon> w) { ... } 
... 
interfaceIsTheArgument(new ArrayList<Gun>()); 
interfaceIsTheArgument(new ArrayList<Knife>()); 

vì các báo cáo chức năng cuộc gọi cuối cùng rằng phương pháp này không áp dụng cho các đối số của nó.

Câu hỏi của tôi là lý do tại sao nếu phương thức biết nó hoạt động ArrayList với giao diện làm loại chung, tại sao không được chuyển vào danh sách mảng dao trong câu lệnh cuối cùng?

+1

Tôi không biết câu trả lời chính xác, nhưng những gì bạn có thể làm cho ví dụ thứ hai của bạn là sử dụng 'public void interfaceIsTheArgument (ArrayList )'. Điều đó sẽ hiệu quả. – fge

Trả lời

13

Để "sửa chữa" các mã, bạn cần phải sử dụng một generic ràng buộc:

public void interfaceIsTheArgument(List<? extends Weapon> w) { ... } 
... 
interfaceIsTheArgument(new ArrayList<? extends Weapon>()); 
interfaceIsTheArgument(new ArrayList<Knife>()); 

Lý do chính là List<Gun>không một lớp con của List<Weapon>. Lý do cho sự thật này có thể được minh họa bằng mã này:

List<Gun> guns = new ArrayList<Gun>(); 
// If List<Weapon> was a super type of List<Gun>, this next line would be allowed 
List<Weapon> weapons = guns; // Won't compile, but let's assume it did 
weapons.add(new Knife()); // Compiles, because Knife is a Weapon 
Gun gun = guns.get(0); // Oops! guns.get(0) is a Knife, not a Gun! 

Bằng việc sử dụng ràng buộc <? extends Weapon>, chúng ta đang nói rằng chúng tôi sẽ chấp nhận bất kỳ loại chung chung mà là một lớp con của Weapon. Sử dụng giới hạn có thể rất mạnh mẽ. Loại giới hạn này là ràng buộc trên trên - chúng tôi đang chỉ định lớp cấp cao nhất là Weapon.

Ngoài ra còn có một thấp ràng buộc, sử dụng cú pháp sau:

List<? super Weapon> // accept any type that is a Weapon or higher in the class hierarchy 

Vì vậy, khi sử dụng mỗi người? Hãy nhớ từ này PECS: "Nhà sản xuất mở rộng, siêu người tiêu dùng". Điều này có nghĩa là bên sản xuất của mã (nơi các đối tượng được tạo) sử dụng extends và ở phía người tiêu dùng của mã (nơi các đối tượng là sử dụng) super. Một khi bạn thử nó một vài lần, bạn sẽ hiểu được trải nghiệm tại sao nó hoạt động tốt.

This SO question/answer bao gồm tốt.

4

Đây là một trong những câu hỏi được đặt nhiều nhất về Generics.Nó một List<Gun> là một List<Weapon>, bạn có thể làm

List<Gun> gunList = new ArrayList<Gun>(); 
List<Weapon> weaponList = gunList; 
weaponList.add(new Knife()); 
gunList.get(0).fire(); // ClassCastException 

này do đó sẽ phá vỡ các loại an toàn hứa generics.

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