2010-06-17 25 views
47

Vì vậy, tôi có một lớp với một constructor như thế này:Tại sao sử dụng Collections.emptySet() với generics làm việc trong nhiệm vụ nhưng không phải là tham số phương thức?

public FilterList(Set<Integer> labels) { 
    ... 
} 

và tôi muốn xây dựng một đối tượng mới FilterList với một tập rỗng. Theo lời khuyên của Joshua Bloch trong cuốn sách Effective Java, tôi không muốn tạo một đối tượng mới cho bộ trống; Tôi sẽ chỉ cần sử dụng Collections.emptySet() thay vì:

FilterList emptyList = new FilterList(Collections.emptySet()); 

này mang lại cho tôi một lỗi, phàn nàn rằng java.util.Set<java.lang.Object> không phải là một java.util.Set<java.lang.Integer>. OK, làm thế nào về điều này:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet()); 

Điều này cũng cho tôi một lỗi! Ok, làm thế nào về điều này:

Set<Integer> empty = Collections.emptySet(); 
FilterList emptyList = new FilterList(empty); 

Hey, nó hoạt động! Nhưng tại sao? Sau khi tất cả, Java không có suy luận kiểu, đó là lý do tại sao bạn nhận được một cảnh báo chuyển đổi không được kiểm soát nếu bạn làm Set<Integer> foo = new TreeSet() thay vì Set<Integer> foo = new TreeSet<Integer>(). Nhưng Set<Integer> empty = Collections.emptySet(); hoạt động mà không cần cảnh báo. Tại sao vậy?

+2

tất cả các câu trả lời dưới đây là chính xác, nhưng những gì tôi không nhận được là: tại sao bạn khởi tạo bộ sưu tập của bạn với một emptyList thay vì gọi một hàm tạo mặc định không có tham số? Mỗi bộ sưu tập mà tôi biết đều có một hàm tạo với một bộ sưu tập hiện có và một hàm tạo rỗng. –

+0

Điều thú vị là DOES biên dịch sau: 'FilterList emptyList = new FilterList ((Set ) (Set ) Collections.emptySet());' – EricS

+1

Một ví dụ khá thú vị: 'Set emptySet = (Set ) Collections.emptySet(); 'không biên dịch. – neo

Trả lời

106

Câu trả lời ngắn gọn là - đó là giới hạn của suy luận kiểu trong hệ thống chung của Java. Nó có thể suy ra các kiểu generic đối với các biến cụ thể, nhưng không thể chống lại các tham số của phương thức.

tôi nghi ngờ này là bởi vì phương pháp được cử động tùy thuộc vào lớp thời gian chạy của các đối tượng sở hữu, vì vậy tại thời gian biên dịch (khi tất cả thông tin chung được giải quyết), bạn có thể không thực sự biết chắc chắn những gì các lớp của tham số phương thức sẽ là và do đó không thể suy ra. Biến khai báo là tốt đẹp và không đổi, vì vậy bạn có thể.

Một người nào khác có thể cung cấp thêm chi tiết và/hoặc liên kết tốt đẹp. :-)

Trong mọi trường hợp, bạn luôn có thể xác định các thông số loại một cách rõ ràng cho các cuộc gọi chung chung như vậy:

Collections.<Integer>emptySet(); 

hoặc thậm chí một vài thông số cùng một lúc, ví dụ

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean> 

Điều này thường có vẻ sạch hơn một chút so với khi truyền, trong trường hợp suy luận không khởi động.

+1

+1 để giải thích rõ lý do tại sao. Tôi muốn ai có thể đánh dấu hai câu trả lời chính xác trong SO :) – jdmichal

+3

Tôi biết trình biên dịch sẽ không nhìn vào lời gọi phương thức cho bối cảnh khi suy ra, nhưng tôi không chắc chắn lý do tại sao. Ít nhất là đối với các phương thức riêng hoặc 'cuối cùng', có thể thực hiện hội thảo. Các phương thức –

+2

@Hank: hoặc 'static', cũng được giải quyết tại thời gian biên dịch. Tuy nhiên - nó * không *, mà tôi đoán là điểm chính. –

5

Bạn muốn làm điều này:

FilterList emptyList = new FilterList(java.util.Collections.<Integer>emptySet()); 

Điều đó nói với các phương pháp emptySet rằng tham số chung của nó một cách rõ ràng nên bởi Integer thay vì mặc định Object. Và vâng, cú pháp hoàn toàn sôi nổi và không trực quan cho việc này. :)

+1

Tôi không biết về cú pháp đó, cảm ơn! Nhưng tôi vẫn tự hỏi tại sao cú pháp đó không cần thiết trong phép gán biến. –

+0

Xem câu trả lời của Andrzej Doyle. Tôi tin rằng đó là một lời giải thích tốt. – jdmichal

+0

Bạn không cần 'mới' ở đó. Trong thực tế, tôi không nghĩ rằng nó sẽ biên dịch với nó. –

6

thử

FilterList emptyList = new FilterList(Collections.<Integer>emptySet()); 

Bạn có thể buộc các tham số kiểu cho các phương pháp đó có chúng, trong trường hợp suy luận là không đủ tốt, hoặc để cho phép bạn sử dụng phân nhóm; ví dụ:

// forces use of ArrayList as parameter instead of the infered List 
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType(); 
Các vấn đề liên quan