Làm cách nào tôi có thể thêm vào Danh sách <? mở rộng Số> cấu trúc dữ liệu?


153

Tôi có một Danh sách được khai báo như thế này:

 List<? extends Number> foo3 = new ArrayList<Integer>();

Tôi đã cố gắng thêm 3 vào foo3. Tuy nhiên tôi nhận được một thông báo lỗi như thế này:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)

61
Lưu ý rằng điều List<? extends Number>đó không có nghĩa là "danh sách các đối tượng thuộc các loại khác nhau, tất cả đều mở rộng Number". Nó có nghĩa là "danh sách các đối tượng của một loại duy nhất mở rộng Number".
Pavel Minaev

1
Bạn nên kiểm tra quy tắc PECS, Nhà sản xuất mở rộng, Siêu người tiêu dùng. stackoverflow.com/questions/2723397/
trộm

Câu trả lời:


303

Xin lỗi, nhưng bạn không thể.

Khai báo ký tự đại diện List<? extends Number> foo3có nghĩa là biến foo3có thể giữ bất kỳ giá trị nào từ một họ các loại (chứ không phải bất kỳ giá trị nào của một loại cụ thể). Nó có nghĩa là bất kỳ trong số này là nhiệm vụ pháp lý:

List<? extends Number> foo3 = new ArrayList<Number>;  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>;  // Double extends Number

Vì vậy, với điều này, loại đối tượng nào bạn có thể thêm vào List foo3đó sẽ hợp pháp sau bất kỳ ArrayListnhiệm vụ nào có thể có ở trên :

  • Bạn không thể thêm một Integerfoo3có thể chỉ vào a List<Double>.
  • Bạn không thể thêm một Doublefoo3có thể chỉ vào a List<Integer>.
  • Bạn không thể thêm một Numberfoo3có thể chỉ vào a List<Integer>.

Bạn không thể thêm bất kỳ đối tượng nào vào List<? extends T>vì bạn không thể đảm bảo loại đối tượng Listđó thực sự đang trỏ đến, vì vậy bạn không thể đảm bảo rằng đối tượng được phép trong đó List. "Đảm bảo" duy nhất là bạn chỉ có thể đọc từ nó và bạn sẽ nhận được một Thoặc lớp con của T.

Logic ngược áp dụng cho super, ví dụ List<? super T>. Đây là hợp pháp:

List<? super Number> foo3 = new ArrayList<Number>; // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>; // Object is a "super" of Number

Bạn không thể đọc loại T cụ thể (ví dụ Number) từ List<? super T>vì bạn không thể đảm bảo loại nào Listthực sự được trỏ đến. "Đảm bảo" duy nhất bạn có là bạn có thể thêm giá trị của loại T(hoặc bất kỳ lớp con nào T) mà không vi phạm tính toàn vẹn của danh sách được trỏ đến.


Ví dụ hoàn hảo về điều này là chữ ký cho Collections.copy():

public static <T> void copy(List<? super T> dest,List<? extends T> src)

Lưu ý cách srckhai báo danh sách sử dụng extendsđể cho phép tôi chuyển bất kỳ Danh sách nào từ một nhóm Danh sách liên quan và vẫn đảm bảo rằng nó sẽ tạo ra các giá trị của loại T hoặc các lớp con của T. Nhưng bạn không thể thêm vào srcdanh sách.

Các destcông dụng khai danh sách supercho phép tôi để vượt qua bất kỳ Danh sách từ một gia đình của các loại sách có liên quan và vẫn đảm bảo tôi có thể viết một giá trị của một loại hình cụ thể T vào danh sách đó. Nhưng nó không thể được đảm bảo để đọc các giá trị của loại T cụ thể nếu tôi đọc từ danh sách.

Vì vậy, bây giờ, nhờ các ký tự đại diện, tôi có thể thực hiện bất kỳ cuộc gọi nào trong số đó bằng phương thức duy nhất đó:

// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double());

Hãy xem xét mã khó hiểu và rất rộng này để rèn luyện trí não của bạn. Các dòng nhận xét là bất hợp pháp và lý do tại sao được nêu ở cực bên phải của dòng (cần phải cuộn để xem một số trong số họ):

  List<Number> listNumber_ListNumber  = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>();                    // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble  = new ArrayList<Double>();                     // error - can assign only exactly <Number>

  List<? extends Number> listExtendsNumber_ListNumber  = new ArrayList<Number>();
  List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
  List<? extends Number> listExtendsNumber_ListDouble  = new ArrayList<Double>();

  List<? super Number> listSuperNumber_ListNumber  = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();      // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble  = new ArrayList<Double>();       // error - Double is not superclass of Number


//List<Integer> listInteger_ListNumber  = new ArrayList<Number>();                  // error - can assign only exactly <Integer>
  List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble  = new ArrayList<Double>();                  // error - can assign only exactly <Integer>

//List<? extends Integer> listExtendsInteger_ListNumber  = new ArrayList<Number>(); // error - Number is not a subclass of Integer
  List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble  = new ArrayList<Double>(); // error - Double is not a subclass of Integer

  List<? super Integer> listSuperInteger_ListNumber  = new ArrayList<Number>();
  List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble  = new ArrayList<Double>();     // error - Double is not a superclass of Integer


  listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List<Number>

  // These next 3 are compile errors for the same reason:
  // You don't know what kind of List<T> is really
  // being referenced - it may not be able to hold an Integer.
  // You can't add anything (not Object, Number, Integer,
  // nor Double) to List<? extends Number>      
//listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>

  listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List<Number> or List<Object>

  listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List<Integer> (duh)

  // This fails for same reason above - you can't
  // guarantee what kind of List the var is really
  // pointing to
//listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List<X> that is only allowed to hold X's

  listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
  listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>

11
Tại sao chúng ta được phép viết một câu như `Danh sách <? mở rộng Số> foo3 = new ArrayList <Integer> (); `nếu chúng ta không thể thêm một phần tử vào danh sách này?
Amit Kumar Gupta

7
@articlestack: Thêm không phải là điều hữu ích duy nhất để làm với một danh sách. Khi một danh sách được điền, việc đọc từ danh sách có thể hữu ích. Ký tự đại diện chung cho phép viết mã hoạt động chung cho một họ danh sách, ví dụ: hoạt động cho cả Danh sách <Integer> hoặc Danh sách <Double>. Ví dụ: nhìn vào chữ ký của Collection.copy (). Đối src Listsố sử dụng extendsđể đọc từ danh sách src, trong khi dest Listđối số sử dụng superđể ghi vào danh sách mệnh. Điều này cho phép một phương thức có thể sao chép từ List<Integer>hoặc List<Double>vào List<Number>hoặc List<Object>.
Bert F

Bert F, xin lỗi vì nhận xét muộn. Có một lỗi đánh máy trong đoạn văn của bạn, "để thêm một giá trị loại T (hoặc lớp con của T)" nên đọc (hoặc siêu lớp của T)?
Vortex

vâng, tôi cũng thấy nó @Vortex Tôi không chắc là nó đúng hay tôi đọc sai.
Ungeheuer

1
@Vortex - or subclass of Tlà chính xác. Ví dụ: tôi không thể thêm một Object(siêu lớp Number) vào List<? super Number> foo3foo3có thể đã được gán là: List<? super Number> foo3 = new ArrayList<Number>(chỉ có thể chứa Numberhoặc các lớp con của Number). <? super Number>đề cập đến các loại List<>s có thể được gán cho foo3- không phải các loại có thể được thêm / đọc từ nó. Các loại thứ có thể được thêm / xóa khỏi foo3phải là những thứ có thể được thêm / xóa khỏi bất kỳ loại List<>nào có thể được gán cho foo3.
Bert F

17

Bạn không thể (không có phôi không an toàn). Bạn chỉ có thể đọc từ họ.

Vấn đề là bạn không biết chính xác danh sách đó là gì. Nó có thể là danh sách của bất kỳ lớp con nào của Số, vì vậy khi bạn cố gắng đưa một phần tử vào đó, bạn không biết rằng phần tử đó thực sự phù hợp với danh sách.

Ví dụ: Danh sách có thể là một danh sách các Bytes, vì vậy sẽ là một lỗi khi đặt Floatnó vào đó.


2

"Danh sách '<'? Số mở rộng> thực sự là một ký tự đại diện giới hạn trên!

Ký tự đại diện giới hạn trên nói rằng bất kỳ lớp nào mở rộng Số hoặc Số chính nó đều có thể được sử dụng làm loại tham số chính thức: Vấn đề bắt nguồn từ thực tế là Java không biết Danh sách loại thực sự là gì. Nó phải là một loại chính xác và độc đáo. Tôi hy vọng nó sẽ giúp :)


1

Bạn có thể làm điều này thay vào đó:

  List<Number> foo3 = new ArrayList<Number>();      
  foo3.add(3);

1

Nó đã gây nhầm lẫn cho tôi mặc dù tôi đã đọc câu trả lời ở đây, cho đến khi tôi tìm thấy bình luận của Pavel Minaev:

Lưu ý rằng Danh sách <? mở rộng Số> không có nghĩa là "danh sách các đối tượng thuộc các loại khác nhau, tất cả đều mở rộng Số". Nó có nghĩa là "danh sách các đối tượng của một loại duy nhất mở rộng Số"

Sau này, tôi đã có thể hiểu lời giải thích tuyệt vời của BertF. Danh sách <? mở rộng Số> có nghĩa là gì? có thể thuộc bất kỳ loại số mở rộng nào (Số nguyên, số kép, v.v.) và không được làm rõ trong khai báo (Danh sách <? số mở rộng> danh sách), vì vậy khi nào bạn muốn sử dụng phương thức thêm thì không biết nếu đầu vào là cùng loại hay không; loại nào vậy?

Vậy các yếu tố của Danh sách <? mở rộng Số> chỉ có thể được đặt khi xây dựng.

Cũng lưu ý điều này: Khi chúng tôi sử dụng các mẫu, chúng tôi sẽ nói với trình biên dịch loại chúng tôi đang làm phiền. T ví dụ giữ loại đó cho chúng tôi, nhưng không ? làm như vậy

Tôi phải nói rằng .. Đây là một trong những điều bẩn thỉu để giải thích / tìm hiểu


-1

trong đó mở rộng danh sách từ 'Object', bạn có thể sử dụng list.add và khi muốn sử dụng list.get chỉ cần truyền Object cho Object của bạn;

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.