Java Generics (Wildcard)

109

Saya punya beberapa pertanyaan tentang wildcard generik di Java:

  1. Apa perbedaan antara List<? extends T>dan List<? super T>?

  2. Apa itu wildcard terbatas dan apa itu wildcard tak terbatas?

Pablo Fernandez
sumber
Jika jawaban Ted Gao dihapus (karena hanya link), berikut adalah posting blog yang ditautkan.
royhowie

Jawaban:

123

Di pertanyaan pertama Anda, <? extends T>dan <? super T>merupakan contoh karakter pengganti berbatas. Karakter pengganti tak terbatas terlihat seperti <?>, dan pada dasarnya berarti <? extends Object>. Ini secara longgar berarti generik dapat jenis apa saja. Karakter pengganti yang dibatasi ( <? extends T>atau <? super T>) menempatkan batasan pada tipe dengan mengatakan bahwa itu harus memperluas tipe tertentu ( <? extends T>dikenal sebagai batas atas), atau harus merupakan leluhur dari tipe tertentu ( <? super T>dikenal sebagai batas bawah) .

Tutorial Java memiliki beberapa penjelasan generik yang cukup bagus di artikel Wildcard dan More Fun with Wildcard .

Bill the Lizard
sumber
Hanya untuk memperbaikinya, jika A <B dan B <C maka: <A meluas C> salah?
Pablo Fernandez
Jika dengan A <B, maksud Anda A meluas B, maka A memperpanjang C. Anda tidak akan menggunakannya dalam sintaks karakter pengganti, Anda akan mengatakan <? meluas C> untuk membatasi pilihan Anda ke A atau B.
Bill the Lizard
dan dalam hal ini jika saya mengatakan <? super C> apa bedanya?
Pablo Fernandez
3
Hanya ingin merekomendasikan referensi lain tentang Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena
3
@ Pablo: <? super C>berarti tipe Anda dibatasi untuk sesuatu Cdi atas dalam hierarki tipe. (Maaf atas balasan yang sangat terlambat. Saya kira kami tidak memiliki pemberitahuan komentar 2 tahun yang lalu?)
Bill the Lizard
49

Jika Anda memiliki hierarki kelas A, B adalah subkelas A, dan C dan D keduanya adalah subkelas B seperti di bawah ini

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Kemudian

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Karakter pengganti terikat seperti di ? extends Bmana B adalah tipe tertentu. Artinya, jenisnya tidak diketahui tetapi "terikat" dapat ditempatkan di atasnya. Dalam hal ini, itu dibatasi oleh beberapa kelas, yang merupakan subkelas dari B.

oxbow_lakes
sumber
Saya berasumsi List<? super B>menggambarkan sebagai Daftar menerima tipe yang merupakan kelas induk dari kelas B ? Itulah mengapa C dan D tidak dapat dikompilasi hmm?
Volkan Güven
38

Josh Bloch juga memiliki penjelasan yang baik tentang kapan harus menggunakan superdan extendsdalam obrolan video google io ini di mana dia menyebutkan mnemonic Produsen extendsKonsumensuper .

Dari slide presentasi:

Misalkan Anda ingin menambahkan metode massal ke Stack<E>

void pushAll(Collection<? extends E> src);

- src adalah produser E.

void popAll(Collection<? super E> dst);

- dst adalah konsumen E.

kosong
sumber
Saya telah membaca buku Bloch, tetapi saya masih tidak dapat melihat perbedaan antara extends dan super dalam kasus khusus ini.
Pablo Fernandez
Tonton videonya, menurut saya cukup jelas. Juga, saya pikir Anda harus mengajukan pertanyaan lain tentang ini "apa perbedaan antara List <? Extends T> dan List <? Super T>" di mana Anda diharapkan akan mendapatkan lebih banyak jawaban. (jika ya, tambahkan tautan dari sini)
kosongkan
2
Tempat tidur - mengapa tidak menyertakan contoh dalam jawaban ini agar lengkap dan berdiri sendiri. Tautan bersifat sementara.
James Schek
3

Ada kalanya Anda ingin membatasi jenis jenis yang diizinkan untuk diteruskan ke parameter jenis. Misalnya, metode yang beroperasi pada angka mungkin hanya ingin menerima instance Number atau subclassnya. Untuk inilah parameter tipe terikat.

Collection<? extends MyObject> 

berarti ia dapat menerima semua objek yang memiliki hubungan IS- A dengan MyObject (yaitu objek apa pun yang merupakan tipe myObject atau kita dapat mengatakan objek apa pun dari subkelas MyObject) atau objek kelas MyObject.

Sebagai contoh:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Kemudian,

Collection<? extends MyObject> myObject; 

hanya akan menerima MyObject atau anak-anak dari MyObject (yaitu setiap objek bertipe OurObject atau YourObject atau MyObject, tetapi bukan objek superclass dari MyObject).

Sandeep Kumar
sumber
1

Secara umum,

Jika suatu struktur mengandung elemen dengan tipe bentuk ? extends E, kita dapat mengeluarkan elemen dari struktur, tetapi kita tidak dapat memasukkan elemen ke dalam struktur

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Untuk memasukkan elemen ke dalam struktur, kita membutuhkan jenis karakter pengganti lain yang disebut Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }
Prateek Joshi
sumber
1

Karakter pengganti umum dibuat untuk membuat metode yang beroperasi di Koleksi lebih dapat digunakan kembali.

Misalnya, jika suatu metode memiliki parameter List<A>, kita hanya dapat memberikannya List<A>kepada metode ini. Ini adalah pemborosan untuk fungsi metode ini dalam beberapa keadaan :

  1. Jika metode ini hanya membaca objek dari List<A>, maka kita harus diizinkan untuk memberikan List<A-sub>metode ini. (Karena A-sub ADALAH A)
  2. Jika metode ini hanya menyisipkan objek ke List<A>, maka kita harus diizinkan untuk memberikan List<A-super>ke metode ini. (Karena A ADALAH A-super)
taoxiaopang
sumber