2014-09-11 19 views
5

Tôi đọc qua phần sau trong hướng dẫn Java: http://docs.oracle.com/javase/tutorial/java/generics/capture.htmlJava Generics Wildcard vs sử dụng Typed Generics

Nó bắt đầu bằng cách nói rằng đoạn mã sau tạo ra một lỗi do thực tế rằng một chụp không thể được chuyển đổi sang một đối tượng để các phương pháp set không thể xác nhận đối tượng đó là kiểu chụp # 1:

import java.util.List; 

public class WildcardError { 

    void foo(List<?> i) { 
     i.set(0, i.get(0)); 
    } 
} 

tôi hơi hiểu lý do đằng sau này. i.get trả về một đối tượng, và trình biên dịch không thể xác định nếu đối tượng là loại hình # 1 vì vậy nó không thể phù hợp với đối số thứ 2 một cách an toàn.

Nó sau đó đề nghị sử dụng đoạn mã sau để thực hiện phương pháp làm việc này:

public class WildcardFixed { 

    void foo(List<?> i) { 
     fooHelper(i); 
    } 


    // Helper method created so that the wildcard can be captured 
    // through type inference. 
    private <T> void fooHelper(List<T> l) { 
     l.set(0, l.get(0)); 
    } 

} 

tôi hơi hiểu tại sao mã này làm việc là tốt, trong mã này, l.get là đảm bảo được các loại T, vì vậy nó có thể được thông qua như là một đối số kiểu T.

Những gì tôi không hiểu là tại sao bạn không thể chỉ cần sử dụng một phương pháp như thế này, không có người giúp đỡ:

class GenericsTest { 
    static <K> void bar(List<K> l) { 
     l.set(0, l.get(l.size() - 1)); 
    } 

    public static void main(String[] args) { 
     List<Integer> lst = Arrays.asList(1, 2, 3, 4); 
     bar(lst); 
     System.out.println(lst); // [4, 3, 2, 4] 
    } 
} 

tức là nếu bạn định sử dụng suy luận kiểu, tại sao không chỉ sử dụng Generics gõ rõ ràng trên ký tự đại diện và chức năng trợ giúp? Có lợi thế nào khi sử dụng ký tự đại diện trong trường hợp này không? Trong trường hợp nào bạn thực sự muốn sử dụng một ký tự đại diện trên một loại chung chung?

+0

'static void' có nghĩa là gì, theo bạn? –

+0

@Mike Với tôi, nó có nghĩa là K là một tham số kiểu chỉ áp dụng cho phạm vi của phương thức. Java sử dụng suy luận kiểu để tìm ra những gì K được cho là đang chạy. Trong ví dụ được hiển thị, tôi chuyển vào một thể hiện của Danh sách để trình biên dịch đưa K trở thành Số nguyên. K có thể là bất kỳ loại tham chiếu nào và các loại tham chiếu mở rộng từ Object, vì vậy tôi xem K là một cách khác để viết một cách rõ ràng trong trường hợp này. – Shashank

+0

@ Giống như tôi không biết ... nó có vẻ giống như một phương pháp void thông thường. Tôi không biết K có liên quan gì với sự trở lại. Tôi chỉ sử dụng cú pháp từ đây: http://docs.oracle.com/javase/tutorial/java/generics/methods.html – Shashank

Trả lời

1

lưu ý đầu tiên mà void foo(List<?> i)void <K> foo(List<K> i) cả chấp nhận các thiết lập chính xác cùng các đối số - giả sử bạn không chỉ định một cách rõ ràng K về vụ việc phương pháp chung chung, bất cứ lập luận cho rằng có thể được thông qua với một chữ ký chức năng có thể được truyền cho và ngược lại. Vì vậy, để "mã bên ngoài", cả hai chữ ký đều hữu ích như nhau.

Giả sử chúng tương đương nhau, thông số có ít tham số kiểu đơn giản hơn và luôn được ưu tiên. Khi trình bày một API công khai tới mã bên ngoài, bạn nên luôn trình bày nó dưới dạng đơn giản nhất, chỉ có những thứ kiểu tối thiểu cần thiết để làm cho nó an toàn. Loại List<?> là đủ để thể hiện rằng nó có thể lấy bất kỳ loại List, vì vậy đó là những gì chúng ta nên sử dụng.

Chúng tôi tình cờ sử dụng tham số loại K nội bộ để giải quyết một số vấn đề generics, nhưng đây chỉ là một chi tiết thực hiện không may bên trong mà mã bên ngoài không cần phải quan tâm. Vì vậy, chúng ta nên che giấu sự xấu xí này và bọc nó trong một chữ ký chức năng đẹp hơn khi tạo một API công khai.

Ngoài các mục đích trừu tượng, một lý do khác tại sao bạn có thể muốn có một chữ ký cụ thể là nếu nó ghi đè lên một phương thức siêu lớp có chữ ký đó. Bạn không thể thêm thông số loại khi ghi đè.

0

Tôi nghĩ ví dụ bạn đề cập trong hướng dẫn đề cập đến các trường hợp xử lý khi bạn cần chấp nhận tham số có ký tự đại diện không liên kết <?>. Đây có thể là trường hợp khi tương tác với mã cũ hoặc mở rộng một lớp hiện có với một loại ký tự đại diện như vậy.

Chủ yếu là bạn sẽ sử dụng những thứ như <T extends U> hoặc <? extends T> khi bạn cần sự linh hoạt nhưng không muốn hy sinh kiểm tra loại.