Java Generics (karakter pengganti yang dibatasi)

Menurut buku "Effective Java" karya Joshua Bloch ada aturan tentang bagaimana/kapan menggunakan wildcard yang dibatasi dalam obat generik. Aturan ini adalah PECS (Producer-Extends, Comsumer-Super). Ketika saya mempelajari contoh berikut:

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);

Saya memahami bahwa aturan ini sangat cocok dengan contoh ini. Saya harus mendeklarasikan metode pushAll seperti contoh berikut:

// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
    {
       push(e);
    }  
}

Tapi apa jadinya jika saya punya contoh berikut?

Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);

Saya harus mendeklarasikan pushAll sebagai berikut:

public void pushAll(Iterable<? super E> src) {
    for (E e : src)
    {  
        push(e);
    }
}

Menurut aturan PECS pernyataan di atas salah. Tapi saya ingin memiliki Stack dari Integers dan meneruskan ke Stack a Number ini. Mengapa tidak melakukannya?
Mengapa saya harus selalu menggunakan kata kunci extends? Mengapa penggunaan super salah?
Tentu saja hal yang sama berlaku untuk sudut pandang komsumen. Mengapa konsumen harus selalu super?


PS: Untuk lebih spesifiknya Anda dapat menemukan contoh di atas pada sektor "Item 28" dari buku yang dirujuk.


person LiTTle    schedule 05.06.2013    source sumber
comment
ketika Anda mendorong - Anda bertindak sebagai konsumen, bukan produsen, jadi Anda perlu menggunakan kata kunci super.   -  person Alexandr    schedule 05.06.2013
comment
Menurut buku saya produser. Jenis komentar Wildcard untuk parameter yang berfungsi sebagai produser E dibuat oleh penulis. Penulis mengatakan bahwa saya adalah seorang konsumen ketika saya menarik Stack! Dua contoh pertama diambil dari buku (copy-paste). Contoh ketiga adalah milik saya.   -  person LiTTle    schedule 05.06.2013


Jawaban (5)


Saat Anda mendeklarasikan Stack<Foo> yang Anda maksud adalah Stack of Foos, atau subkelas dari Foo. Sebagai contoh, Anda diharapkan dapat memasukkan String ke dalam Stack<Object>. Cara lain tidak benar, Anda seharusnya tidak dapat memasukkan Objek lain, ke dalam Stack<String>.

Dalam contoh Anda, Anda mendeklarasikan Stack<Integer>. Anda seharusnya dapat memasukkan bilangan bulat ke dalam tumpukan ini, tetapi bukan bilangan lain (seperti Ganda), seperti yang Anda lakukan jika Anda mendeklarasikan parameter <? super E>. Itu sebabnya metode put harus memiliki parameter tipe <? extends E>.

person Aleksander Blomskøld    schedule 05.06.2013

Mencoba menyimpan angka arbitrer dalam Stack tidak mungkin berhasil, karena Angka bisa berupa sesuatu selain Integer. Jadi contoh Anda tidak masuk akal.

Anda akan menggunakan super ketika objek berperan sebagai konsumen, yaitu ketika instance tipe generik objek diteruskan sebagai argumen ke metode objek. Misalnya:

 Collections.sort(List<T>, Comparator<? super T>)

Dalam contoh ini, metode pengurutan mengambil T instance dari koleksi, dan meneruskannya sebagai argumen ke compare(T o1, T o2) pembanding.

Bandingkan ini dengan contoh pertama Anda, di mana Iterable src adalah produser. Metode pushAll() memanggil metode Iterable yang menghasilkan (yaitu mengembalikan) instance T. Dalam hal ini, iterable adalah produser, oleh karena itu penggunaan ? extends T

person JB Nizet    schedule 05.06.2013

Dalam metode pushAll, Anda tidak meneruskan tipe E, tetapi tipe apa pun yang memperluas E. Jadi, alih-alih meneruskan Iterable dari Numbers, Anda dapat meneruskan Iterable jenis apa pun yang memperluas Number.

Contoh asli menggunakan tipe Number karena Anda kemudian dapat meneruskan tipe apa pun yang merupakan subkelas dari Number, seperti Integer, BigDecimal dan seterusnya.

Dalam contoh Anda, Anda melakukannya sebaliknya. Anda menggunakan Integer untuk mendeklarasikan Stack Anda. Oleh karena itu, pushAll hanya dapat menerima kelas-kelas yang diperpanjang oleh Integer. Anda tidak akan dapat menggunakan Numbers (atau kelas lainnya, karena Integer adalah kelas final).

person ipavlic    schedule 05.06.2013

Hal pertama yang perlu diperhatikan adalah Integer memperluas Angka, jadi Anda tidak boleh memasukkan objek Angka ke dalam Tumpukan Integer. Namun, sampel pertama akan bekerja dengan Integer, Floats, BigDecimal, dan semua subkelas Number lainnya.

person Martin    schedule 05.06.2013

Contoh Anda tidak masuk akal. Konstruk seperti <? extends Number> berarti Angka dan setiap tipe yang mewarisi Angka diperbolehkan. Jadi Anda menentukan batas atas dan bawah, dari tipe Nomor hingga yang paling spesifik. Sebaliknya, <? super Number> berarti Angka dan superti apa pun diperbolehkan. Karena Number memperluas Objek dan mengimplementasikan Serializable, tiga tipe berikut diperbolehkan:

  1. java.lang.Nomor
  2. java.lang.Objek
  3. java.io.Serializable

Dalam contoh Anda, Anda mendeklarasikan tipe generik Stack<Integer>. Mari kita pertimbangkan hal berikut ini.

  1. Tumpukan Anda tidak akan pernah bisa menampung item tipe super Integer apa pun
  2. Stack Anda tidak akan pernah dapat menampung item dari subtipe Integer apa pun, karena kelas Integer bersifat final sehingga tidak dapat disubklasifikasikan.

Jadi, jika Anda ingin mendeklarasikan tipe generik Stack<Integer>, iterable Anda bertipe Iterable<Integer> sehingga Stack Anda hanya dapat menampung item bertipe Integer. Anda sepenuhnya benar dengan PECS mnemonik, tetapi ini hanya berfungsi jika Anda telah memilih tipe konkret yang memiliki setidaknya satu tipe super dan setidaknya satu subtipe.

person My-Name-Is    schedule 05.06.2013