2016-02-27 29 views
6

Tuy nhiên, một người mới, cố gắng để hiểu Java Generics. Tôi đã quan sát tất cả các chủ đề, tôi đã tìm thấy, nhưng tôi vẫn có những câu hỏi rất lớn. Ông có thể giải thích cho tôi những điều sau đây:<? mở rộng Class> và <? Super Class> trong Java - tại sao nó hoạt động theo cách này?

  1. <? extends SomeClass> phương tiện, mà ? là "bất kỳ loại", và extends SomeClass phương tiện, điều này bất kỳ loại có thể chỉ là một lớp con của SomeClass. OK, tôi viết hai lớp tiểu học:
abstract class Person { 
    private String name; 
    public Person(String name) { 
     this.name = name; 
    } 
} 

class Student extends Person { 
    public Student(String name) { 
     super(name); 
    } 
} 

Lớp Student sẽ ? trong ví dụ của chúng tôi. ? extends Person, chính xác. Sau đó, tôi đang cố gắng để thêm học sinh mới đến ArrayList, mà, như tôi hiểu từ viết ở trên, áp dụng tất cả các lớp học, đó là lớp con của Person:

Student clarissa = new Student("Clarissa Starling"); 
List<? extends Person> list = new ArrayList<>(); 
list.add(clarissa); //Does not compile 

Eclipse nói:

"Các phương thức add (capture # 3-of? mở rộng Person) trong các loại Danh sách không áp dụng cho các đối số (sinh viên)"

Làm thế nào lớp Student có thể không áp dụng , khi chúng tôi khai báo Danh sách, được thông báo bởi <? extends Person>Student chính xác mở rộng lớp Person?

Tuy nhiên, đoạn mã sau:

List<? super Person> list = new ArrayList<>(); 
list.add(clarissa); 

biên dịch và hoạt động tốt (list.get(0), truyền cho println phương pháp, chỉ cho tôi đúng kết quả của toString gọi). Như tôi đã hiểu, List<? super Person> có nghĩa là tôi có thể chuyển sang danh sách này bất kỳ loại nào, đó là loại siêu cho lớp học Person của chúng tôi (trong trường hợp của chúng tôi là Object chỉ lớp học). Nhưng chúng ta thấy rằng, trái với logic, chúng ta có thể dễ dàng thêm Học sinh lớp con vào số List<? super Person> của chúng ta!

OK, gạt sang một bên cảm xúc của mình, và xem nào, điều gì có thể xảy ra với Clarissa Starling trong bộ sưu tập của chúng tôi. Chúng ta hãy cùng lớp của chúng tôi Student, và thêm một vài phương pháp để nó:

class Student extends Person { 
    private int grant; 
    public Student(String name) { 
     super(name); 
    } 

    public void setGrant(int grant) { 
     this.grant = grant; 
    } 

    public int getGrant() { 
     return this.grant; 
    } 

} 

Sau đó, chúng tôi vượt qua một đối tượng, khởi tạo từ lớp đầy mới mẻ này (đối tượng của chúng tôi "Clarissa", ví dụ), để List<? extends Person>. Làm điều này, chúng tôi có nghĩa là, chúng ta có thể lưu trữ phân lớp trong bộ sưu tập của các siêu lớp của nó. Có lẽ, tôi không hiểu một số ý tưởng cơ bản, nhưng ở giai đoạn này tôi không thấy bất kỳ sự khác biệt nào giữa việc thêm phân lớp vào tập hợp các siêu lớp của nó và việc gán tham chiếu đến đối tượng "clarissa" vào biến, đánh máy. Chúng ta có cùng phương pháp giảm các phương thức invokable, khi chúng ta muốn đối xử với một trong số chúng, sử dụng biến superclass của chúng ta. Vì vậy, tại sao List<? extends SomeClass> không hoạt động theo cùng một cách, trong đó List<? super SomeClass> hoạt động ngược lại?

  1. Tôi không hiểu sự khác biệt giữa fundamenthal <T> (hoặc <E>, hoặc bất kỳ lá thư khác từ phần thích hợp của JLS) và <?>.Cả hai <T><?>loại chủ, vậy tại sao chúng tôi có hai "từ khóa" (ký hiệu này KHÔNG phải từ khóa, tôi vừa sử dụng từ này để nhấn mạnh ý nghĩa nặng nề của cả hai ký hiệu bằng ngôn ngữ Java).
+1

Giả sử bạn có hai lớp 'Student' &' Teacher' mở rộng 'Person'. Bây giờ, bạn đã tạo danh sách 'List 'và giả sử bạn đã thêm' Student' vào nó rồi làm thế nào bạn sẽ chắc chắn rằng 'list.get (0)' sẽ trả về bạn chỉ 'Student' chứ không phải' Teacher'? Để ngăn chặn sự mơ hồ này, nó không được phép. – user2004685

+2

Đọc http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs –

+0

Tôi mong đợi, list.get (0) đó sẽ trả về một đối tượng, nó sẽ được đưa vào kiểu siêu lớp, và tôi ' sẽ chỉ có thể gọi các phương thức, được khai báo trong superclass (Person). –

Trả lời

10

Cách tôi xem xét vấn đề này - trình giữ chỗ T là viết tắt của một loại xác định và ở những nơi chúng tôi cần biết loại thực tế chúng tôi cần để có thể làm việc. Ngược lại, ký tự đại diện ? có nghĩa là bất kỳ loại nào và tôi sẽ không bao giờ cần phải biết loại đó là gì. Bạn có thể sử dụng giới hạn extendssuper để giới hạn ký tự đại diện đó theo một cách nào đó nhưng không có cách nào để nhận loại thực tế.

Vì vậy, nếu tôi có List<? extends MySuper> thì tất cả những gì tôi biết về nó là mọi đối tượng trong nó thực hiện giao diện MySuper và tất cả các đối tượng trong danh sách đó cùng loại. Tôi không biết loại đó là gì, chỉ có một số loại phụ của MySuper. Điều đó có nghĩa là tôi có thể lấy các đối tượng ra khỏi danh sách đó miễn là tôi chỉ cần sử dụng giao diện MySuper. Những gì tôi không thể làm là đặt các đối tượng vào danh sách bởi vì tôi không biết loại đó là gì - trình biên dịch sẽ không cho phép nó bởi vì ngay cả khi tôi có một đối tượng đúng kiểu, nó không thể chắc chắn tại thời gian biên dịch. Vì vậy, bộ sưu tập theo ý nghĩa là một bộ sưu tập chỉ đọc.

Logic hoạt động theo cách khác khi bạn có List<? super MySuper>. Ở đây chúng tôi đang nói rằng bộ sưu tập thuộc loại xác định là siêu kiểu của MySuper. Điều này có nghĩa là bạn luôn có thể thêm đối tượng MySuper vào đối tượng đó. Những gì bạn không thể làm, bởi vì bạn không biết loại thực tế, là lấy các đối tượng từ nó. Vì vậy, bây giờ bạn đã có một loại bộ sưu tập chỉ ghi.

Nơi bạn sử dụng ký tự đại diện bị ràng buộc so với tham số loại chung 'chuẩn' là nơi giá trị của sự khác biệt bắt đầu trở nên rõ ràng. Giả sử tôi có 3 lớp học Person, StudentTeacher, với Person là cơ sở mà StudentTeacher mở rộng. Trong một API, bạn có thể viết một phương thức lấy một tập hợp gồm Person và thực hiện điều gì đó cho mọi mục trong bộ sưu tập. Đó là tốt, nhưng bạn thực sự chỉ quan tâm rằng bộ sưu tập là của một số loại đó là tương thích với giao diện Person - nó sẽ làm việc với List<Student>List<Teacher> tốt như nhau. Nếu bạn xác định phương thức như thế này

public void myMethod(List<Person> people) { 
    for (Person p: people) { 
     p.doThing(); 
    } 
} 

thì không thể chụp List<Student> hoặc List<Teacher>. Vì vậy, thay vào đó, bạn sẽ xác định nó để có List<? extends Person> ...

public void myMethod(List<? extends Person> people){ 
    for (Person p: people) { 
     p.doThing(); 
    } 
} 

Bạn có thể làm điều đó bởi vì myMethod không bao giờ cần phải thêm vào danh sách. Và bây giờ bạn thấy rằng List<Student>List<Teacher> đều có thể được chuyển vào phương thức.

Bây giờ, giả sử bạn có một phương pháp khác muốn thêm Sinh viên vào danh sách. Nếu tham số phương thức mất List<Student> thì nó không thể mất List<People> mặc dù điều đó sẽ ổn. Vì vậy, bạn thực hiện nó như là một List<? super Student> ví dụ:

public void listPopulatingMethod(List<? extends Student> source, List<? super Student> sink) { 
    for (Student s: source) { 
     sink.add(s); 
    } 
} 

Đây là trái tim của PECS, bạn có thể đọc chi tiết hơn ở nơi khác ... What is PECS (Producer Extends Consumer Super)? http://www.javacodegeeks.com/2011/04/java-generics-quick-tutorial.html

+0

Cảm ơn bạn rất nhiều, câu trả lời của bạn là tuyệt vời! Bây giờ tôi cảm thấy bản thân mình gần gũi hơn với sự hiểu biết về Generics trong Java. Ngoài ra, tôi đã bỏ qua sự kiện, không giống như các lớp 'Person' và' Student', không có quan hệ thừa kế giữa 'Person' và' Student' trong 'List ' và 'List ' (như tôi hiểu, nó thực sự không mối quan hệ giữa các loại tham số trong Danh sách này do loại xóa?). –

+0

Vâng, do đó, xóa-erasure có nghĩa là 'dưới mui xe' họ tất cả chỉ là danh sách tại thời gian chạy. Để duy trì an toàn kiểu, trình biên dịch cần phải khớp với tất cả các tham số kiểu chung và đảm bảo rằng mọi thứ đều tương thích. Vì vậy, các mối quan hệ thừa kế trong các tham số kiểu generic kết thúc làm việc hơi khác so với các thứ có thể được xác minh bằng kiểm tra kiểu thời gian chạy thuần túy. – sisyphus

+0

Tôi vẫn không hiểu tại sao bạn không thể thêm, ví dụ, một 'new Dog()' vào một 'List '. Không thể trình biên dịch thấy rằng lớp chó mở rộng động vật? – Ogen

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