2012-03-07 48 views
8

Đối với một giao diện chung:Ký tự đại diện kiểu Java của Java thực sự có ý nghĩa gì? Sự khác biệt thực sự giữa Foo và Foo <?> là gì?

public interface Foo<T> { 
    void f(T t); 
} 

Sự khác biệt giữa hai lĩnh vực:

public class Bar { 
    Foo foo1; 
    Foo<?> foo2; 
} 

foo2 là một loại generic và foo thì không. Vì ? là một ký tự đại diện (mà tôi nghĩ là bất kỳ loại nào) và mọi loại là một loại đối tượng, sau đó tôi đã mong đợi Foo<?>Foo<Object> tương đương ngữ nghĩa và cú pháp.

Tuy nhiên, hãy kiểm tra như sau:

public class Puzzler { 
    void f() { 
     Integer i = null; 
     Foo<?> foo1 = null; 
     foo1.foo(i); // ERROR 
     Foo foo2 = null; 
     foo2.foo(i); // OKAY 
     Foo<Integer> foo3 = null; 
     foo3.foo(i); // OKAY 
     Foo<Object> foo4 = null; 
     foo4.foo(i); // OKAY 
    } 

    private interface Foo<T> { 
     void foo(T t); 
    } 
} 

Vì vậy Foo<?>Foo<Object>không cùng cú pháp.

Điều gì đang xảy ra ở đây? Tôi khá mắc kẹt khi cố hiểu điều này.

+0

giả định đây là lỗi biên dịch? – paislee

+0

@andrewcooke Mã đó sẽ hợp lệ; tuy nhiên, vì đối tượng sẽ được lưu trữ trong biến 'Foo ', nó sẽ không biết về tham số 'Chuỗi'. Do đó, nếu 'Foo' có bất kỳ phương thức nào lấy đối số của kiểu' T', thì giá trị được phép duy nhất sẽ là 'null' và nếu nó có bất kỳ phương thức nào trả về' T', thì nó được coi là 'Object'. – Taymon

+0

xin lỗi, đã xóa nhận xét của tôi trước khi tôi thấy câu trả lời của bạn. bây giờ đang bối rối. khi nào bắt công việc? –

Trả lời

6

Foo<?> là ngữ nghĩa giống như Foo<? extends Object>: nó là một Foo với kiểu tham số của một cái gì đó cụ thể, nhưng điều duy nhất biết về "cái gì đó" là nó là một số lớp con của Object (mà không nói quá nhiều, kể từ tất cả các lớp là các lớp con của Object). Mặt khác, Foo<Object> là một số Foo với thông số loại cụ thể Object. Mặc dù mọi thứ đều tương thích với bài tập với Object, nhưng không phải mọi thứ đều tương thích với bài tập với ? trong đó ? mở rộng Object.

Dưới đây là một ví dụ về lý do tại sao Foo<?> nên tạo ra một lỗi:

public class StringFoo implements Foo<String> { 
    void foo(String t) { . . . } 
} 

Bây giờ thay đổi ví dụ của bạn như thế này:

Foo<?> foo1 = new StringFoo(); 

Kể từ i là một Integer, không có cách nào mà trình biên dịch nên cho phép foo1.foo(i) để biên dịch.

Lưu ý rằng

Foo<Object> foo4 = new StringFoo(); 

cũng sẽ không biên dịch theo các quy tắc cho matching parameterized types từ ObjectString là provably loại riêng biệt.

Foo (không có thông số loại ở tất cả — loại thô) thường được coi là lỗi lập trình. Tuy nhiên, theo số Java Language Specification (§4.8), trình biên dịch chấp nhận mã như vậy để không phá vỡ mã thừa kế không chung chung.

type erasure, không có điều nào tạo ra sự khác biệt khi tạo mã byte. Đó là, sự khác biệt duy nhất giữa chúng là lúc biên dịch.

+0

xóa hoàn toàn nhầm lẫn, nhờ – mwsltn

+0

Trình biên dịch * không thể * xử lý các loại thô dưới dạng lỗi, xem [JLS] (http://docs.oracle.com /javase/specs/jls/se7/html/jls-4.html#jls-4.8) –

+0

@Daniel - Trong Eclipse, bạn có thể thiết lập trình biên dịch để xử lý việc sử dụng các kiểu thô dưới dạng lỗi. Bạn cũng có thể thiết lập hầu hết các trình biên dịch để xử lý tất cả các cảnh báo như các lỗi và trình biên dịch có thể chắc chắn cảnh báo về việc sử dụng các kiểu thô. –

0

Vâng, do loại xóa, bất kỳ điều gì liên quan đến generics chỉ là thời gian biên dịch; Tôi đoán đó là những gì bạn đang gọi cú pháp.
Tôi nghĩ rằng đánh giá ban đầu của bạn là chính xác và sự khác biệt thực sự là làm cho nó một biến chung loại và Foo đồng bằng không.

0

Hãy xem xét các loại sau:

  • List<Object>
  • List<CharSequence>
  • List<String>

Mặc dù String là một subtype của CharSequence mà là một subtype của Object, các loại sách không có bất kỳ mối quan hệ siêu loại phụ nào. (Tò mò, String[] một subtype của CharSequence[] mà là một subtype của Object[], nhưng đó là vì những lý do lịch sử.)

Giả sử chúng ta muốn viết một phương pháp mà in một danh sách. Nếu chúng ta làm

void print(List<Object> list) {...} 

này sẽ không thể in một List<String> (không có hacks), kể từ khi một List<String> không phải là một List<Object>. Nhưng với các ký tự đại diện, chúng tôi có thể viết

void print(List<?> list) {...} 

và chuyển nó bất kỳ Danh sách nào.

Ký tự đại diện có thể có giới hạn trên và dưới để tăng cường tính linh hoạt. Giả sử chúng tôi muốn in danh sách chỉ chứa CharSequence s. Nếu chúng ta làm

void print(List<CharSequence> list) {...} 

sau đó chúng ta gặp phải cùng một vấn đề - chúng tôi chỉ có thể vượt qua nó một List<CharSequence>, và List<String> của chúng tôi không phải là một List<CharSequence>. Nhưng nếu chúng ta thay vì làm

void print(List<? extends CharSequence> list) {...} 

Sau đó, chúng ta có thể chuyển thông tin này một List<String>, và một List<StringBuilder>, và vân vân.

0

Ký tự đại diện trong Foo<?> cho biết rằng trong phạm vi hiện tại, bạn không biết hoặc quan tâm đến loại 'Foo' nào của bạn.

Foo<?>Foo<? extends Object> giống nhau (dấu đầu tiên là viết tắt cho cách khác). Foo<Object> là khác nhau.

Một ví dụ cụ thể:

Bạn có thể gán bất kỳ loại List để List<?> ví dụ

List<?> list1 = new ArrayList<String>(); 
List<?> list2 = new ArrayList<Object>(); 
List<?> list3 = new ArrayList<CharSequence>(); 

Nếu bạn có một List<?> bạn có thể gọi size() bởi vì bạn không cần phải biết những gì loại danh sách đó là để tìm hiểu kích thước của nó.Và bạn có thể gọi get(i) vì chúng tôi biết rằng danh sách chứa một số loại Object, do đó trình biên dịch sẽ xử lý nó như thể get trả về và Object.
Nhưng bạn không thể gọi add(o) vì bạn không biết (và trình biên dịch không biết) loại danh sách bạn đang xử lý.
Trong ví dụ của chúng tôi ở trên, bạn sẽ không muốn cho phép list1.add(new Object()); vì đó là nghĩa vụ phải được một danh sách các String s

Lý do cho ký tự đại diện như vậy bạn có thể làm những việc như thế này:

public static boolean containsNull(List<?> list) 
{ 
    for(Object o : list) 
    { 
     if(o == null) return true; 
    } 
    return false; 
} 

Đó là mã có thể làm việc trên bất kỳ loại danh sách mà bạn muốn, một List<String>, List<Object>, List<Integer> vv

Nếu chữ ký là public static boolean containsNull(List<Object> list) sau đó bạn chỉ có thể vượt qua List<Object> với nó, List<String> sẽ không hoạt động.

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