2012-10-24 29 views
13

Tại sao cố gắng để biên dịchTrộn các tham số kiểu lồng nhau và ký tự đại diện trong Java

public class GenericsFail { 
    public static void main(String[] args) { 
     accept(new HashMap<String, List<String>>()); 
    } 

    public static void accept(Map<String, List<?>> multiMap) {} 
} 

cung cấp cho các lỗi

GenericsFail.java:7: error: method accept in class GenericsFail cannot be applied to given types; 
       accept(new HashMap<String, List<String>>()); 
       ^
    required: Map<String,List<?>> 
    found: HashMap<String,List<String>> 
    reason: actual argument HashMap<String,List<String>> cannot be converted to Map<String,List<?>> by method invocation conversion 

Các ký tự đại diện chỉ được phép nếu nó không được lồng vào bên trong List.

+0

thể trùng lặp của [Nested Generics với ký tự đại diện] (http: // stackoverflow .com/questions/1341093/lồng nhau-generics-with-wildcards) – artbristol

Trả lời

3

Để trở thành tổng quát hơn

void accept(Map<String, ? extends List<?>> multiMap) 
+0

Tôi nghĩ rằng tốt hơn thể hiện ý định của tôi. Bạn có thể giúp tôi hiểu tại sao tôi cần ký tự đại diện bị ràng buộc trong đó không? – hertzsprung

+0

trong ví dụ đơn giản hơn, giả sử bạn muốn chấp nhận một tập hợp các số.'accept (Set )' quá hẹp, nó sẽ không chấp nhận một 'Set '. bạn cần 'accept (Set )', sau đó nó có thể chấp nhận 'Set , Set , Đặt ' vv – irreputable

+0

Nếu tôi đã viết 'accept (new HashMap >())' Tôi có thể hiểu tại sao ký tự đại diện bị ràng buộc là cần thiết, vì 'ArrayList' là một kiểu con của' List'. Nhưng trong câu hỏi này, tôi vẫn chưa rõ. Có lẽ tôi sẽ không bao giờ hiểu đầy đủ generics ... ;-) – hertzsprung

11

Nguyên nhân là do các ? trong List<?> có thể là "bất cứ điều gì", nhưng một khác nhau "bất cứ điều gì" trong mỗi Map nhập cảnh. Tức là, nó sẽ chấp nhận một số List<String> trong một mục nhập và một số khác là List<Integer>.

Nhưng bạn đang đi qua trong một Map có loại cùng của List trong mỗi entry, vì vậy loại không bị ràng buộc trong cùng một cách hay để cùng mức độ tự do.

Các "sửa chữa" là để khóa các loại đến một loại hình cụ thể, nhưng vẫn là "bất cứ điều gì" - chỉ là cùng "bất cứ điều gì * trong mỗi entry, bằng cách gõ phương pháp:

public static <T> void accept(Map<String, List<T>> multiMap) // complies 

hoặc nếu phương pháp của bạn thực sự không cần phải biết loại, sử dụng một ký tự đại diện để bọc các loại:

public static void accept(Map<String, ? extends List<?>> multiMap) // compiles 

phiên bản mới nhất này hoạt động vì các loại danh sách, mặc dù là một ký tự đại diện, được cố định một un đã biết, nhưng nhất quán, hãy nhập khi được gọi.


Tôi tìm phiên bản đã nhập dễ đọc hơn (và mã) và loại có sẵn để sử dụng nếu bạn quyết định sau đó phương pháp của bạn cần biết loại.

+0

Aha, nó hiển nhiên bây giờ bạn nói nó. Có lẽ tôi đã bỏ lỡ giải pháp bởi vì, trong mã thực tế của tôi, tôi không quan tâm đến những gì List chứa kể từ khi tôi chỉ cần gọi toString() trên các phần tử. – hertzsprung

+0

Lưu ý rằng hai chữ ký trên có sự khác biệt khác. ' void chấp nhận (Bản đồ > multiMap)' sẽ không chấp nhận 'Bản đồ >'; tức là, 'Danh sách ' yêu cầu một loại được xác định cụ thể cho Danh sách <>. Nó cũng sẽ không chấp nhận các loại phụ của Danh sách <> (ví dụ: Arraylist ) trừ khi chúng được "truyền" dưới dạng Danh sách . Tuy nhiên, 'void accept (Bản đồ > multiMap)' ** sẽ ** chấp nhận 'Bản đồ >'; nghĩa là, nó sẽ chấp nhận _any_ type (hoặc subtype) của danh sách. –

2

Dưới đây là một ví dụ ngắn về việc tại sao các compilator không thể chấp nhận tham số này:

public static void main(String[] args) { 
     //if this would be accepted here ... 
     accept(new HashMap<String, List<String>>()); 
    } 

    public static void accept(Map<String, List<?>> multiMap) { 
     //this is valid considering the type of multiMap parameter, 
     //but would not work with the actual passed parameter. 
     multiMap.put("test", new ArrayList<Integer>()); 
    } 
Các vấn đề liên quan