Sự khác biệt giữa là gì? và Object trong Java generic?


137

Tôi đang sử dụng Eclipse để giúp tôi dọn sạch một số mã để sử dụng chung chung Java. Hầu hết thời gian nó làm một công việc tuyệt vời là suy ra các loại, nhưng có một số trường hợp loại suy ra phải càng chung chung càng tốt: Đối tượng. Nhưng Eclipse dường như đang cho tôi một tùy chọn để lựa chọn giữa một loại Đối tượng và một loại '?'.

Vì vậy, sự khác biệt giữa:

HashMap<String, ?> hash1;

HashMap<String, Object> hash2;

4
Xem hướng dẫn chính thức về Wildcards . Nó giải thích điều đó tốt và đưa ra một ví dụ về lý do tại sao nó lại cần thiết hơn khi sử dụng Object.
Ben S

Câu trả lời:


148

Một ví dụ của HashMap<String, String>trận đấu Map<String, ?>nhưng không Map<String, Object>. Giả sử bạn muốn viết một phương thức chấp nhận ánh xạ từ Strings sang bất cứ thứ gì: Nếu bạn sẽ viết

public void foobar(Map<String, Object> ms) {
    ...
}

bạn không thể cung cấp a HashMap<String, String>. Nếu bạn viết

public void foobar(Map<String, ?> ms) {
    ...
}

nó hoạt động

Một điều đôi khi bị hiểu nhầm trong các khái quát của Java là đó List<String>không phải là một kiểu con List<Object>. (Tuy nhiên, String[]trên thực tế là một subtype của Object[], đó là một trong những lý do tại sao Generics và mảng không trộn đều. (Mảng trong Java là hiệp biến, Generics không, họ là bất biến )).

Mẫu: Nếu bạn muốn viết một phương pháp mà chấp nhận Lists của InputStreams và phân nhóm của InputStream, bạn muốn viết

public void foobar(List<? extends InputStream> ms) {
    ...
}

Nhân tiện: Java hiệu quả của Joshua Bloch là một tài nguyên tuyệt vời khi bạn muốn hiểu những điều không đơn giản trong Java. (Câu hỏi của bạn ở trên cũng được đề cập rất tốt trong cuốn sách.)


1
Đây có phải là cách đúng để sử dụng FeedbackEntity <?> ở cấp độ bộ điều khiển cho tất cả các chức năng của bộ điều khiển của tôi không?
Irakli

Câu trả lời hoàn hảo!
gaurav

36

Một cách khác để suy nghĩ về vấn đề này là

HashMap<String, ?> hash1;

tương đương với

HashMap<String, ? extends Object> hash1;

Kết hợp kiến ​​thức này với "Nguyên tắc Nhận và Đặt" trong phần (2.4) từ Bộ sưu tập và Tổng hợp Java :

Nguyên tắc Nhận và Đặt: sử dụng ký tự đại diện mở rộng khi bạn chỉ nhận các giá trị ngoài cấu trúc, sử dụng siêu ký tự khi bạn chỉ đặt giá trị vào cấu trúc và không sử dụng ký tự đại diện khi cả hai nhận và đặt.

và thẻ hoang dã có thể bắt đầu có ý nghĩa hơn, hy vọng.


1
Nếu "?" làm bạn bối rối, "? extends Object" có thể sẽ khiến bạn bối rối hơn. Có lẽ.
Michael Myers

Cố gắng cung cấp "công cụ tư duy" để cho phép một người suy luận về chủ đề khó khăn này. Cung cấp thông tin bổ sung về việc mở rộng ký tự đại diện.
Julien Chastang

2
Cảm ơn thông tin bổ sung. Tôi vẫn đang tiêu hóa nó. :)
skiphoppy

HashMap<String, ? extends Object> Vì vậy, nó chỉ ngăn chặn nullđược thêm vào trong hashmap?
mallaudin

12

Thật dễ hiểu nếu bạn nhớ rằng đó Collection<Object>chỉ là một bộ sưu tập chung có chứa các đối tượng loại Object, nhưng Collection<?>là một loại siêu của tất cả các loại bộ sưu tập.


1
Có một điểm cần làm là điều này không chính xác dễ dàng ;-), nhưng nó đúng.
Sean Reilly

6

Các câu trả lời trên hiệp phương sai bao gồm hầu hết các trường hợp nhưng bỏ lỡ một điều:

"?" là bao gồm "Đối tượng" trong hệ thống phân cấp lớp. Bạn có thể nói rằng String là một loại Object và Object là một loại ?. Không phải mọi thứ đều khớp với Object, nhưng mọi thứ đều khớp?

int test1(List<?> l) {
  return l.size();
}

int test2(List<Object> l) {
  return l.size();
}

List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1);  // compiles because any list will work
test1(l2);  // compiles because any list will work
test2(l1);  // fails because a ? might not be an Object
test2(l2);  // compiled because Object matches Object

4

Bạn không thể đặt bất cứ thứ gì vào một cách an toàn Map<String, ?>, bởi vì bạn không biết loại giá trị nào được cho là.

Bạn có thể đặt bất kỳ đối tượng nào vào a Map<String, Object>, vì giá trị được biết là an Object.


"Bạn không thể đặt bất cứ thứ gì vào Bản đồ <String,?>" Sai. Bạn CÓ THỂ, đó là mục đích của nó.
Ben S

3
Ben sai, giá trị duy nhất bạn có thể đưa vào bộ sưu tập loại <?> Là null, trong khi bạn có thể đặt bất cứ thứ gì vào bộ sưu tập loại <Object>.
sk.

2
Từ liên kết tôi đã đưa ra câu trả lời của mình: "Vì chúng tôi không biết loại phần tử của c là viết tắt của cái gì, chúng tôi không thể thêm các đối tượng vào nó." Tôi xin lỗi vì thông tin sai lệch.
Ben S

1
vấn đề lớn nhất là tôi không hiểu làm sao bạn có thể thêm bất cứ thứ gì vào HashMap <String,?>, nếu chúng tôi coi rằng MỌI THỨ trừ các nguyên thủy là các đối tượng. Hay là cho người nguyên thủy?
avalon

1
@avalon Ở nơi khác, có một tham chiếu đến bản đồ đó bị giới hạn. Ví dụ, nó có thể là một Map<String,Integer>. Chỉ Integercác đối tượng nên được lưu trữ trong bản đồ dưới dạng giá trị. Nhưng vì bạn không biết loại giá trị (đó là ?), bạn không biết nếu nếu nó an toàn để gọi put(key, "x"), put(key, 0)hoặc bất cứ điều gì khác.
erickson

2

Khai báo hash1như là một HashMap<String, ?>mệnh lệnh rằng biến hash1có thể chứa bất kỳ HashMapcái nào có khóa Stringvà bất kỳ loại giá trị nào.

HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();

Tất cả những điều trên là hợp lệ, bởi vì biến map có thể lưu trữ bất kỳ bản đồ băm nào. Biến đó không quan tâm loại Giá trị là gì, của hàm băm mà nó giữ.

Có một ký tự đại diện không không , tuy nhiên, cho phép bạn đặt bất kỳ loại đối tượng vào bản đồ của bạn. như một vấn đề thực tế, với bản đồ băm ở trên, bạn không thể đặt bất cứ thứ gì vào đó bằng mapbiến:

map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");

Tất cả các lệnh gọi phương thức trên sẽ dẫn đến lỗi thời gian biên dịch vì Java không biết loại Giá trị của HashMap bên trong maplà gì.

Bạn vẫn có thể nhận được một giá trị từ bản đồ băm. Mặc dù bạn "không biết loại giá trị" (vì bạn không biết loại bản đồ băm nào bên trong biến của mình), bạn có thể nói rằng mọi thứ đều là một lớp con của Objectvà, vì vậy, bất cứ điều gì bạn thoát khỏi bản đồ sẽ thuộc loại Object:

HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.

myMap.put("ABC", 10);

HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).

System.out.println(output);

Khối mã trên sẽ in 10 đến bàn điều khiển.


Vì vậy, để kết thúc, hãy sử dụng một HashMapký tự đại diện khi bạn không quan tâm (ví dụ: không quan trọng) các loại của HashMapchúng là gì, ví dụ:

public static void printHashMapSize(Map<?, ?> anyMap) {
    // This code doesn't care what type of HashMap is inside anyMap.
    System.out.println(anyMap.size());
}

Nếu không, chỉ định các loại mà bạn cần:

public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
    for (int i = 'A'; i <= 'Z'; i++)
        System.out.println(anyCharacterMap.get((char) i));
}

Trong phương pháp trên, chúng ta cần biết rằng khóa của Bản đồ là Character, nếu không, chúng ta sẽ không biết nên sử dụng loại nào để nhận các giá trị từ nó. toString()Tuy nhiên, tất cả các đối tượng đều có một phương thức, do đó bản đồ có thể có bất kỳ loại đối tượng nào cho các giá trị của nó. Chúng tôi vẫn có thể in các giá trị.

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.