Dấu hỏi trong tham số loại chung của Java có nghĩa là gì?


216

Đây là một đoạn mã nhỏ được lấy từ một số ví dụ đi kèm với Trình phân tích cú pháp Stanford. Tôi đã phát triển Java được khoảng 4 năm, nhưng chưa bao giờ hiểu rõ về kiểu mã này được cho là gì.

List<? extends HasWord> wordList = toke.tokenize();

Tôi không lo lắng về các chi tiết của mã. Điều tôi bối rối là chính xác những gì mà biểu thức chung được cho là truyền đạt, bằng tiếng Anh.

Ai đó có thể giải thích điều này với tôi?


Câu trả lời:


226
? extends HasWord

có nghĩa là "Một lớp / giao diện mở rộng HasWord." Nói cách khác, HasWordchính nó hoặc bất kỳ đứa con nào của nó ... về cơ bản là bất cứ thứ gì có thể hoạt động cùng với instanceof HasWordcộng null.

Nói một cách kỹ thuật hơn, ? extends HasWordlà một ký tự đại diện giới hạn, được nêu trong Mục 31 của Phiên bản Java hiệu quả thứ 3 , bắt đầu từ trang 139. Chương tương tự từ Phiên bản 2 có sẵn trực tuyến dưới dạng PDF ; phần trên các ký tự giới hạn là Mục 28 bắt đầu từ trang 134.

Cập nhật: Liên kết PDF đã được cập nhật kể từ khi Oracle gỡ bỏ nó một lúc trước. Bây giờ nó chỉ đến bản sao được lưu trữ bởi Trường Kỹ thuật Điện tử và Khoa học Máy tính của Đại học Queen Mary, London.

Cập nhật 2: Hãy đi sâu vào chi tiết hơn một chút về lý do tại sao bạn muốn sử dụng ký tự đại diện.

Nếu bạn khai báo một phương thức mà chữ ký của bạn mong đợi bạn truyền vào List<HasWord>, thì điều duy nhất bạn có thể truyền vào là a List<HasWord>.

Tuy nhiên, nếu nói chữ ký là List<? extends HasWord>sau đó bạn có thể vượt qua trong một List<ChildOfHasWord>thay thế.

Lưu ý rằng có một sự khác biệt tinh tế giữa List<? extends HasWord>List<? super HasWord>. Như Joshua Bloch đã nói: PECS = nhà sản xuất mở rộng, siêu người tiêu dùng.

Điều này có nghĩa là nếu bạn chuyển qua một bộ sưu tập mà phương thức của bạn lấy dữ liệu từ đó (tức là bộ sưu tập đang tạo ra các phần tử cho phương thức của bạn để sử dụng), bạn nên sử dụng extends. Nếu bạn chuyển qua một bộ sưu tập mà phương thức của bạn thêm dữ liệu vào (tức là bộ sưu tập đang tiêu thụ các yếu tố mà phương thức của bạn tạo ra), thì nó nên sử dụng super.

Điều này nghe có vẻ khó hiểu. Tuy nhiên, bạn có thể nhìn thấy nó trong List's sortlệnh (mà chỉ là một shortcut lên phiên bản hai arg của Collections.sort). Thay vì lấy một Comparator<T>, nó thực sự mất một Comparator<? super T>. Trong trường hợp này, Trình so sánh đang sử dụng các phần tử của Listthứ tự để sắp xếp lại Danh sách.


17
"bất cứ điều gì sẽ làm việc với instanceof" - cộng với các giá trị null. Hãy nhớ rằng giá trị null trả về false cho bất kỳ kiểm tra thể hiện nào.
Eyal Schneider

12
Đừng quên giao diện. Các ? không phải đại diện cho một lớp!
Mark Peters

9
List<HasWord>là chính xác những gì bạn mô tả: Một danh sách chứa bất kỳ đối tượng xnào x instanceof HasWordtrả về true và null. Bạn không cần một ký tự đại diện cho điều này. Ký tự đại diện có nghĩa là nó thực sự có thể là một danh sách của một loại khác, miễn là loại này là một kiểu con HasWord. (Xin lỗi vì nhận xét muộn.)
Paŭlo Ebermann

1
Liên kết PDF dường như không hoạt động, nhưng điều này rất hữu ích với tôi: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062

Thật kỳ lạ, tôi chưa bao giờ nhận được một tin nhắn vào năm ngoái khi @ user1338062 đăng và không biết liên kết PDF không hoạt động. Đã sửa bây giờ. Tôi cũng đã thêm một liên kết đến cuốn sách.
Powerlord

65

Một dấu hỏi là một dấu hiệu cho 'bất kỳ loại nào'. ?một mình nghĩa là

Bất kỳ loại mở rộng Object(bao gồm Object)

trong khi ví dụ của bạn ở trên có nghĩa là

Bất kỳ kiểu mở rộng hoặc thực hiện HasWord(bao gồm cả HasWordnếu HasWordlà một lớp không trừu tượng)


2
vậy có public Set<Class<?>> getClasses()nghĩa là gì? Sự khác biệt với Set<Class>cái gì? Tôi đang nhìn javax.ws.rs.core.Application.
hầm

14

List<? extends HasWord>chấp nhận bất kỳ lớp cụ thể nào mở rộng HasWord. Nếu bạn có các lớp sau ...

public class A extends HasWord { .. }
public class B extends HasWord { .. }
public class C { .. }
public class D extends SomeOtherWord { .. }

... wordListCHỈ có thể chứa danh sách As hoặc Bs hoặc hỗn hợp của cả hai vì cả hai lớp đều mở rộng cùng một cha mẹ hoặc null(không thể kiểm tra thể hiện HasWorld).


8
Không chỉ cụ thể, các lớp con trừu tượng.
bloparod

2
Không chỉ các lớp con cụ thể và trừu tượng, mà cả các giao diện con : List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Mark Peters

2
Bạn không cần ký tự đại diện cho điều này, thực sự: List<HasWord>cũng tốt cho việc này. Vấn đề ở đây là biến này có thể chứa a List<A>hoặc List<B>cũng như a List<HasWord>.
Paŭlo Ebermann

12

Có lẽ một ví dụ "thế giới thực" giả tạo sẽ giúp ích.

Tại nơi làm việc của tôi, chúng tôi có thùng rác có hương vị khác nhau. Tất cả các thùng chứa rác, nhưng một số thùng là chuyên gia và không lấy tất cả các loại rác. Vì vậy, chúng tôi có Bin<CupRubbish>Bin<RecylcableRubbish>. Hệ thống loại cần đảm bảo rằng tôi không thể đặt tôi HalfEatenSandwichRubbishvào một trong hai loại này, nhưng nó có thể đi vào thùng rác chung Bin<Rubbish>. Nếu tôi muốn nói về một Bintrong số Rubbishđó có thể là chuyên ngành để tôi không thể bỏ rác không tương thích, thì đó sẽ là Bin<? extends Rubbish>.

(Lưu ý: ? extendskhông có nghĩa là chỉ đọc. Ví dụ, tôi có thể với các biện pháp phòng ngừa thích hợp lấy ra một mẩu rác từ thùng rác không xác định và sau đó đặt nó trở lại ở một nơi khác.)

Không chắc chắn bao nhiêu mà giúp. Con trỏ trỏ đến sự hiện diện của đa hình không hoàn toàn rõ ràng.


5

Bằng tiếng Anh:

Đó là một Listloại nào đó mở rộng lớp HasWord, bao gồmHasWord

Nói chung, ?trong khái quát có nghĩa là bất kỳ lớp học. Và extends SomeClassxác định rằng đối tượng đó phải mở rộng SomeClass(hoặc là lớp đó).


1
Trên thực tế, thay vì lớp . Điều này cũng hoạt động tốt cho các giao diện.
Paŭlo Ebermann

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.