2013-07-31 45 views
8

Tại sao Java không ném bất kỳ cảnh báo khi biên dịch của tôi TestGenericsclass, xem xét rằng Stringclass được final và không thể mở rộng?Java Generics wildcard mở rộng lớp thức

import java.util.*; 
    public class TestGenerics { 
     public void addStrings(List<? extends String> list) { 
      // some code here 
     } 
    } 
} 
+2

Tại sao bạn nghĩ điều đó nên? – jason

+0

@ L4zy Khi tôi cố gắng sử dụng phương pháp tôi nhận được cảnh báo ...Ngoài ra, bạn cũng có thể chuyển mã 'Uncompilable source code - Loại không hợp lệ:' –

Trả lời

7

Hãy nói rằng tôi đã có một phương pháp như thế này:

public List<? extends T> filterOutNulls(List<T> input) { ... 

Cấp, không phải là chữ ký tốt nhất trên thế giới, nhưng vẫn hoàn toàn hợp pháp. Điều gì sẽ xảy ra nếu tôi đã chuyển một số List<String> cho phương thức đó? Theo chữ ký, nó trả về một List<? extends String>. Nếu Java không cho phép loại đó, nó sẽ không thể sử dụng phương thức này cho List<String> (hoặc ít nhất, nó sẽ không thể sử dụng giá trị trả về).

secondarily, cú pháp extends vẫn còn hữu ích trong trường hợp này, vì List<String>List<? extends String> có những hạn chế khác nhau - đặc biệt, bạn không thể thêm bất cứ điều gì nhưng một null đen để List<? extends String>. Thỉnh thoảng tôi sẽ sử dụng ? extends để biểu thị rằng bộ sưu tập là chỉ đọc (vì chỉ T mà bạn có thể chuyển là null) và ? super để biểu thị chỉ ghi (vì bạn chỉ có thể nhận được T s là Object). Đây không phải là hoàn toàn đánh lừa bằng chứng (bạn vẫn có thể gọi loại bỏ phương pháp, vượt qua trong null s, downcast, vv) nhưng phục vụ như một lời nhắc nhở nhẹ nhàng về cách bộ sưu tập có thể có nghĩa là để được sử dụng.

+0

Điều đó có ý nghĩa không, cảm ơn lời giải thích. – L4zy

+0

Có thể có trường hợp sử dụng để trả về danh sách ký tự đại diện bị chặn như thế này nhưng tôi thường tránh nó. Các đối tượng được trả về bởi một số phương thức rời khỏi vùng đóng gói của lớp của chúng và cần được xem là thuộc sở hữu của nơi gọi là phương thức. Nếu bạn muốn cấp quyền truy cập hạn chế cho một số danh sách nội bộ trả về 'Collections.unmodifiableList()'/có một phương thức như 'YourObject.addToInternalList (SomeData)'. Điều đó thực thi việc sử dụng chính xác và bạn có thể [Giữ các ký tự đại diện bị giới hạn trong các giá trị trả lại] (http://www.ibm.com/developerworks/library/j-jtp07018/#4.0). – zapl

+0

@zapl Vấn đề về phong cách, tôi cho là vậy. Nếu bạn trả về một 'Collections.unmodifiableList', không có gì để chỉ ra rằng trong chữ ký kiểu. Vì vậy, bây giờ tôi có thể điều tôi có một bản sao của danh sách, và sau đó tôi sẽ nhận được một 'UnsupportedOperationException' khi tôi cố gắng sửa đổi nó - nó giao dịch verbosity của một loại tham chiếu (tại trang web cuộc gọi) cho loại an toàn. Sẽ tốt hơn nếu JDK có một siêu liên kết của mỗi lớp sưu tập mà chỉ có các phương pháp đọc, nhưng nó không có; về cơ bản, tôi (đôi khi) sử dụng 'Danh sách 'như' ReadList 'của một người nghèo', vì sau này không tồn tại. – yshavit

6

Trình biên dịch không thực sự lưu ý thực tế đó, bởi vì nó không quan trọng. String s vẫn được phép trong danh sách và trong sản phẩm cuối cùng, không thể tìm thấy bất kỳ điều gì mở rộng String. Sau erasure, nó đi ra như thế này:

public void addStrings(List list) 

Như bạn có thể thấy, hiện nay không có lời đề nghị của một lớp mở rộng String. Nếu bạn tạo một lớp mở rộng String, đó sẽ là lỗi biên dịch. Không cần javac phải lo lắng về điều đó.

+3

Bằng cách đó, bạn có thể chuyển một 'List ' sang phương thức mong đợi một 'List ', kể từ khi xóa chúng 'Danh sách'. Nó chắc chắn đúng với bytecode, nhưng toàn bộ quan điểm của generics là cung cấp an toàn loại * ở trên và xa hơn nữa * mã byte nào có thể cung cấp. – yshavit

+0

Không, xóa không loại bỏ bất kỳ thông tin loại nào khỏi chữ ký phương thức. Trong bytecode, phương thức sẽ có một * chữ ký chung * của "<: + Ljava/lang/String;> (Ljava/util/List ;) V" (hoặc một cái gì đó như thế này). –

+0

@ Rogério Tôi cần phải kiểm tra xem – tbodt

0

Các hệ thống kiểu không xem xét List<String>List<? extends String> tương đương, mặc dù tại thời điểm biên dịch, String không có phân nhóm khác hơn là chính nó, do đó bất kỳ đối tượng đó là một List<? extends String> cũng phải là một List<String>.

Một cách giải thích là final không phải là cuối cùng - đó là ok để loại bỏ final từ một lớp học và không có gì nên phá vỡ: http://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.2

Đó là không phải không có ưu tiên mà các hệ thống kiểu mất final xem xét; ví dụ: chúng tôi không thể truyền một số String đến Runnable, vì trình biên dịch cho rằng nếu đối tượng là String, nó không thể là một số lớp con không xác định thực hiện Runnable.

Nếu chúng ta muốn generics cũng làm cho lý luận như thế và suy ra rằng List<String>List<? extends String> là tương đương, nó sẽ làm cho các quy tắc gõ phức tạp hơn.

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