Apa itu PECS (Produser Memperpanjang Super Konsumen)?


729

Saya menemukan PECS (kependekan dari Produser extendsdan Konsumensuper ) sambil membaca tentang obat generik.

Dapatkah seseorang menjelaskan kepada saya cara menggunakan Pecs untuk menyelesaikan kebingungan antara extendsdan super?


3
Penjelasan yang sangat bagus dengan contoh @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 yang menjelaskan superbagian tetapi, memberikan gagasan tentang yang lain.
lupchiazoem

Jawaban:


844

tl; dr: "PECS" adalah dari sudut pandang koleksi. Jika Anda hanya menarik item dari koleksi generik, itu adalah produsen dan Anda harus menggunakannya extends; jika Anda hanya memasukkan barang, itu adalah konsumen dan Anda harus menggunakannya super. Jika Anda melakukan keduanya dengan koleksi yang sama, Anda tidak boleh menggunakan salah satu extendsatau super.


Misalkan Anda memiliki metode yang menjadikan kumpulan parameter sebagai parameternya, tetapi Anda ingin agar lebih fleksibel daripada hanya menerima a Collection<Thing>.

Kasus 1: Anda ingin melihat-lihat koleksi dan melakukan hal-hal dengan setiap item.
Maka daftar adalah produsen , jadi Anda harus menggunakan a Collection<? extends Thing>.

Alasannya adalah bahwa a Collection<? extends Thing>dapat menampung subtipe apa pun Thing, dan dengan demikian setiap elemen akan berperilaku seperti Thingketika Anda melakukan operasi Anda. (Anda sebenarnya tidak dapat menambahkan apa pun ke a Collection<? extends Thing>, karena Anda tidak dapat mengetahui saat runtime subtipe tertentu dari Thingkoleksi tersebut.)

Kasus 2: Anda ingin menambahkan sesuatu ke koleksi.
Maka daftar tersebut adalah konsumen , jadi Anda harus menggunakan a Collection<? super Thing>.

Alasannya di sini adalah bahwa tidak seperti Collection<? extends Thing>, Collection<? super Thing>selalu dapat menahan Thingtidak peduli apa jenis parameter yang sebenarnya. Di sini Anda tidak peduli apa yang sudah ada dalam daftar selama itu akan memungkinkan Thinguntuk ditambahkan; inilah yang ? super Thingmenjamin.


142
Saya selalu mencoba memikirkannya dengan cara ini: Seorang produser diperbolehkan untuk menghasilkan sesuatu yang lebih spesifik, karenanya memperluas , seorang konsumen diperbolehkan untuk menerima sesuatu yang lebih umum, maka dari itu super .
Feuermurmel

10
Cara lain untuk mengingat perbedaan produsen / konsumen adalah dengan memikirkan metode tanda tangan. Jika Anda memiliki metode doSomethingWithList(List list), Anda mengkonsumsi daftar dan karenanya perlu kovarians / luas (atau Daftar invarian). Di sisi lain jika metode Anda List doSomethingProvidingList, maka Anda menghasilkan Daftar dan akan memerlukan contravariance / super (atau Daftar invarian).
Raman

3
@MichaelMyers: Mengapa kita tidak bisa menggunakan tipe parameter saja untuk kedua kasus ini? Apakah ada keuntungan spesifik menggunakan wildcard di sini, atau itu hanya cara meningkatkan keterbacaan mirip dengan, katakanlah, menggunakan referensi constsebagai parameter metode dalam C ++ untuk menandakan bahwa metode tersebut tidak mengubah argumen?
Chatterjee

7
@Aman, saya pikir Anda hanya bingung. Di doSthWithList (Anda dapat memiliki List <? Super Thing>), karena Anda seorang konsumen, Anda dapat menggunakan super (ingat, CS). Namun, itu Daftar <? extends Thing> getList () karena Anda diizinkan untuk mengembalikan sesuatu yang lebih spesifik saat memproduksi (PE).
masterxilo

4
@AZ_ Saya membagikan sentimen Anda. Jika suatu metode mendapatkan () dari daftar, metode tersebut akan dianggap sebagai Konsumen <T>, dan daftar tersebut dianggap sebagai penyedia; tetapi aturan Pecs adalah "dari sudut pandang daftar", oleh karena itu 'meluas' diperlukan. Itu harus GEPS: dapatkan ekstensi; menempatkan super.
Treefish Zhang

561

Prinsip di balik ini dalam ilmu komputer disebut

  • Kovarian: ? extends MyClass,
  • Kontravarian: ? super MyClassdan
  • Invarian / non-varians: MyClass

Gambar di bawah ini harus menjelaskan konsepnya. Foto milik: Andrey Tyukin

Kovarian vs Kontravarian


144
Hai semuanya. Saya Andrey Tyukin, saya hanya ingin mengkonfirmasi bahwa anoopelias & DaoWen menghubungi saya dan mendapatkan izin untuk menggunakan sketsa, itu dilisensikan di bawah (CC) -BY-SA. Thx @ Anoop untuk memberinya kehidupan kedua ^^ @Brian Agnew: (pada "beberapa suara"): Itu karena ini adalah sketsa untuk Scala, ia menggunakan sintaksis Scala dan mengasumsikan varian situs deklarasi, yang sangat berbeda dengan panggilan aneh Jawa -situs varians ... Mungkin saya harus menulis jawaban yang lebih rinci yang dengan jelas menunjukkan bagaimana sketsa ini berlaku untuk Jawa ...
Andrey Tyukin

3
Ini adalah salah satu penjelasan paling sederhana dan paling jelas untuk Covariance dan Contravariance yang pernah saya temukan!
cs4r

@Andrey Tyukin Hai, saya juga ingin menggunakan gambar ini. Bagaimana saya bisa menghubungi Anda?
slouc

Jika Anda memiliki pertanyaan tentang ilustrasi ini, kita dapat mendiskusikannya di ruang obrolan: chat.stackoverflow.com/rooms/145734/...
Andrey Tyukin


49

PECS (Produser extendsdan Konsumen super)

mnemonic → prinsip Get and Put.

Prinsip ini menyatakan bahwa:

  • Gunakan wildcard extends ketika Anda hanya mendapatkan nilai dari struktur.
  • Gunakan super wildcard ketika Anda hanya memasukkan nilai ke dalam struktur.
  • Dan jangan gunakan wildcard saat Anda berdua mendapatkan dan memasukkan.

Contoh di Jawa:

class Super {

    Object testCoVariance(){ return null;} //Covariance of return types in the subtype.
    void testContraVariance(Object parameter){} // Contravariance of method arguments in the subtype.
}

class Sub extends Super {

    @Override
    String testCoVariance(){ return null;} //compiles successfully i.e. return type is don't care(String is subtype of Object) 
    @Override
    void testContraVariance(String parameter){} //doesn't support even though String is subtype of Object

}

Prinsip substitusi Liskov: jika S adalah subtipe dari T, maka objek tipe T dapat diganti dengan objek tipe S.

Di dalam sistem jenis bahasa pemrograman, aturan pengetikan

  • kovarian jika mempertahankan pemesanan jenis (≤), yang memesan jenis dari yang lebih spesifik ke yang lebih umum;
  • contravariant jika membalikkan pemesanan ini;
  • invarian atau nonvariant jika keduanya tidak berlaku.

Kovarian dan contravariance

  • Tipe data baca-saja (sumber) dapat bersifat kovarian ;
  • tipe data hanya-tulis (sink) dapat bersifat contravarian .
  • Jenis data yang bisa berubah yang bertindak sebagai sumber dan sumber data harus invarian .

Untuk menggambarkan fenomena umum ini, pertimbangkan jenis array. Untuk tipe Hewan kita bisa membuat tipe Hewan []

  • kovarian : Kucing [] adalah Hewan [];
  • contravarian : Hewan [] adalah Kucing [];
  • invarian : Hewan [] bukan Kucing [] dan Kucing [] bukan Hewan [].

Contoh Java:

Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)

List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime  

lebih banyak contoh

wildcard dibatasi (yaitu menuju ke suatu tempat) : Ada 3 rasa wildcard yang berbeda:

  • In-variance / Non-variance: ?atau ? extends Object- Tidak terbatas Wildcard Tidak . Itu singkatan dari semua jenis keluarga. Gunakan saat Anda berdua mendapatkan dan meletakkan.
  • Co-variance: ? extends T(keluarga dari semua jenis yang merupakan subtipe dari T) - wildcard dengan batas atas . Tadalah kelas paling atas dalam hierarki warisan. Gunakan extendswildcard ketika Anda hanya mendapatkan nilai dari struktur.
  • Contra-variance: ? super T(keluarga dari semua jenis yang merupakan supertipe T) - wildcard dengan batas bawah . Tadalah kelas paling bawah dalam hierarki warisan. Gunakan superwildcard ketika Anda hanya Masukan nilai-nilai ke dalam struktur.

Catatan: wildcard ?berarti nol atau satu kali , mewakili tipe yang tidak dikenal. Wildcard dapat digunakan sebagai tipe parameter, tidak pernah digunakan sebagai argumen tipe untuk pemanggilan metode generik, pembuatan instance kelas generik. (Yaitu ketika wildcard yang digunakan untuk referensi yang tidak digunakan di tempat lain dalam program seperti yang kita gunakan T)

masukkan deskripsi gambar di sini

class Shape { void draw() {}}

class Circle extends Shape {void draw() {}}

class Square extends Shape {void draw() {}}

class Rectangle extends Shape {void draw() {}}

public class Test {
 /*
   * Example for an upper bound wildcard (Get values i.e Producer `extends`)
   * 
   * */  

    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Shape()); // Error:  is not applicable for the arguments (Shape) i.e. inheritance is not supporting
        list.add(new Circle()); // Error:  is not applicable for the arguments (Circle) i.e. inheritance is not supporting
        list.add(new Square()); // Error:  is not applicable for the arguments (Square) i.e. inheritance is not supporting
        list.add(new Rectangle()); // Error:  is not applicable for the arguments (Rectangle) i.e. inheritance is not supporting
        Shape shape= list.get(0);//compiles so list act as produces only

        /*You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape> 
         * You can get an object and know that it will be an Shape
         */         
    }
      /* 
* Example for  a lower bound wildcard (Put values i.e Consumer`super`)
* */
    public void testContraVariance(List<? super Shape> list) {
        list.add(new Shape());//compiles i.e. inheritance is supporting
        list.add(new Circle());//compiles i.e. inheritance is  supporting
        list.add(new Square());//compiles i.e. inheritance is supporting
        list.add(new Rectangle());//compiles i.e. inheritance is supporting
        Shape shape= list.get(0); // Error: Type mismatch, so list acts only as consumer
        Object object= list.get(0); // gets an object, but we don't know what kind of Object it is.

        /*You can add a Shape,Circle,Square,Rectangle to a List<? super Shape> 
        * You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
        */  
    }
}

generik dan contoh


Hei, saya hanya ingin tahu apa yang Anda maksud dengan kalimat terakhir: "Jika Anda pikir analogi saya salah, mohon perbarui". Apakah maksud Anda jika secara etis salah (yang subjektif) atau jika salah dalam konteks pemrograman (yang objektif: tidak, tidak salah)? Saya ingin menggantinya dengan contoh yang lebih netral yang dapat diterima secara universal terlepas dari norma-norma budaya dan kepercayaan etis; Jika itu tidak masalah dengan Anda.
Neuron

akhirnya saya bisa mendapatkannya. Penjelasan yang bagus.
Oleg Kuts

2
@ Primrem,, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.saya tidak bisa menambahkan elemen ke Daftar <?> Atau Daftar <? meluas Object>, jadi saya tidak mengerti mengapa itu bisa terjadi Use when you both get and put.
LiuWenbin_NO.

1
@LiuWenbin_NO. - Bagian dari jawaban itu menyesatkan. ?- "wildcard tak terbatas" - sesuai dengan kebalikan dari invarian. Silakan merujuk ke dokumentasi berikut: docs.oracle.com/javase/tutorial/java/generics/… yang menyatakan: Dalam kasus di mana kode perlu mengakses variabel sebagai variabel "dalam" dan "keluar", lakukan tidak menggunakan wildcard. (Mereka menggunakan "in" dan "out" sebagai sinonim dengan "get" dan "put"). Dengan pengecualian nullAnda tidak dapat menambahkan ke Koleksi dengan parameter ?.
mouselabs

29
public class Test {

    public class A {}

    public class B extends A {}

    public class C extends B {}

    public void testCoVariance(List<? extends B> myBlist) {
        B b = new B();
        C c = new C();
        myBlist.add(b); // does not compile
        myBlist.add(c); // does not compile
        A a = myBlist.get(0); 
    }

    public void testContraVariance(List<? super B> myBlist) {
        B b = new B();
        C c = new C();
        myBlist.add(b);
        myBlist.add(c);
        A a = myBlist.get(0); // does not compile
    }
}

Jadi "? Extends B" harus ditafsirkan sebagai "? B extends". Itu adalah sesuatu yang diperluas B sehingga akan mencakup semua kelas super B hingga Objek, tidak termasuk B itu sendiri. Terima kasih untuk kodenya!
Saurabh Patil

3
@SaurabhPatil Tidak, ? extends Bberarti B dan apa pun yang memperpanjang B.
asgs

24

Seperti yang saya jelaskan di jawaban saya ke pertanyaan lain, Pecs adalah perangkat mnemonik yang dibuat oleh Josh Bloch untuk bantuan ingat P roducer extends, C onsumer super.

Ini berarti bahwa ketika tipe parameter yang dilewatkan ke suatu metode akan menghasilkan instance T(mereka akan diambil darinya dengan beberapa cara),? extends T harus digunakan, karena setiap instance dari subkelas Tjuga a T.

Ketika tipe parametered yang diteruskan ke suatu metode akan mengkonsumsi instance T(mereka akan diteruskan untuk melakukan sesuatu), ? super Tharus digunakan karena instance Tdapat secara legal diteruskan ke metode apa pun yang menerima beberapa supertype dari T. A Comparator<Number>dapat digunakan pada Collection<Integer>, misalnya. ? extends Ttidak akan berfungsi, karena a Comparator<Integer>tidak dapat beroperasi pada aCollection<Number> .

Perhatikan bahwa secara umum Anda hanya boleh menggunakan ? extends Tdan ? super Tuntuk parameter beberapa metode. Metode seharusnya hanya digunakan Tsebagai parameter tipe pada tipe pengembalian generik.


1
Apakah prinsip ini hanya berlaku untuk Koleksi? Masuk akal ketika seseorang mencoba menghubungkannya dengan daftar. Jika Anda berpikir tentang tanda tangan sortir (List <T>, Comparator <? Super T>) ---> di sini Comparator menggunakan super sehingga itu berarti konsumen dalam konteks Pecs. Ketika Anda melihat implementasi misalnya seperti: public int bandingkan (Orang a, Orang b) {return a.age <b.age? -1: a.age == b.age? 0: 1; } Saya merasa seperti Orang tidak mengkonsumsi apa pun selain menghasilkan usia. Itu membuat saya bingung. Apakah ada kesalahan dalam alasan saya atau Pecs hanya berlaku untuk Koleksi?
Fatih Arslan

24

Singkatnya, tiga aturan mudah diingat PECS:

  1. Gunakan <? extends T>wildcard jika Anda perlu mengambil objek tipe Tdari koleksi.
  2. Gunakan <? super T>wildcard jika Anda perlu memasukkan objek jenis Tdalam koleksi.
  3. Jika Anda perlu memuaskan kedua hal itu, yah, jangan gunakan wildcard apa pun. Sesimpel itu.

10

mari kita asumsikan hierarki ini:

class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C

Mari kita perjelas PE - Produser Memperpanjang:

List<? extends Shark> sharks = new ArrayList<>();

Mengapa Anda tidak dapat menambahkan objek yang memperpanjang "Hiu" dalam daftar ini? Suka:

sharks.add(new HammerShark());//will result in compilation error

Karena Anda memiliki daftar yang bisa tipe A, B atau C saat runtime , Anda tidak bisa menambahkan objek tipe A, B atau C di dalamnya karena Anda bisa berakhir dengan kombinasi yang tidak diperbolehkan di java.
Dalam praktiknya, kompiler memang dapat melihat pada saat bersamaan Anda menambahkan B:

sharks.add(new HammerShark());

... tetapi tidak memiliki cara untuk mengetahui apakah pada saat runtime, B Anda akan menjadi subtipe atau supertipe dari tipe daftar. Saat runtime, tipe daftar bisa berupa tipe A, B, C. Jadi, Anda tidak dapat menambahkan HammerSkark (tipe super) di daftar DeadHammerShark misalnya.

* Anda akan berkata: "OK, tapi mengapa saya tidak bisa menambahkan HammerSkark di dalamnya karena ini adalah jenis terkecil?". Jawab: Ini yang terkecil yang kamu tahu. Tapi HammerSkark dapat diperpanjang juga oleh orang lain dan Anda berakhir dalam skenario yang sama.

Mari kita perjelas CS - Consumer Super:

Dalam hierarki yang sama kita dapat mencoba ini:

List<? super Shark> sharks = new ArrayList<>();

Apa dan mengapa Anda dapat menambahkan ke daftar ini?

sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());

Anda dapat menambahkan jenis objek di atas karena apa pun di bawah hiu (A, B, C) akan selalu menjadi subtipe dari apa pun di atas hiu (X, Y, Z). Mudah dimengerti.

Anda tidak dapat menambahkan jenis di atas Hiu, karena pada saat runtime jenis objek yang ditambahkan dapat lebih tinggi dalam hierarki daripada jenis daftar yang dinyatakan (X, Y, Z). Ini tidak diizinkan

Tetapi mengapa Anda tidak dapat membaca dari daftar ini? (Maksud saya Anda bisa mendapatkan elemen dari itu, tetapi Anda tidak bisa menugaskannya selain dari Objek o):

Object o;
o = sharks.get(2);// only assignment that works

Animal s;
s = sharks.get(2);//doen't work

Saat runtime, tipe daftar dapat berupa tipe apa pun di atas A: X, Y, Z, ... Compiler dapat mengkompilasi pernyataan penugasan Anda (yang tampaknya benar) tetapi, pada saat runtime tipe s (Hewan) dapat lebih rendah di hierarki dari jenis daftar yang dinyatakan (yang bisa berupa Makhluk, atau lebih tinggi). Ini tidak diizinkan

Untuk menyimpulkan

Kami gunakan <? super T>untuk menambahkan objek bertipe sama atau di bawah Tke List. Kita tidak bisa membacanya.
Kami menggunakan <? extends T>untuk membaca objek jenis sama atau di bawah Tdari daftar. Kami tidak dapat menambahkan elemen ke dalamnya.


9

(menambahkan jawaban karena tidak pernah cukup contoh dengan wildcard Generics)

       // Source 
       List<Integer> intList = Arrays.asList(1,2,3);
       List<Double> doubleList = Arrays.asList(2.78,3.14);
       List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);

       // Destination
       List<Integer> intList2 = new ArrayList<>();
       List<Double> doublesList2 = new ArrayList<>();
       List<Number> numList2 = new ArrayList<>();

        // Works
        copyElements1(intList,intList2);         // from int to int
        copyElements1(doubleList,doublesList2);  // from double to double


     static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
        for(T n : src){
            dest.add(n);
         }
      }


     // Let's try to copy intList to its supertype
     copyElements1(intList,numList2); // error, method signature just says "T"
                                      // and here the compiler is given 
                                      // two types: Integer and Number, 
                                      // so which one shall it be?

     // PECS to the rescue!
     copyElements2(intList,numList2);  // possible



    // copy Integer (? extends T) to its supertype (Number is super of Integer)
    private static <T> void copyElements2(Collection<? extends T> src, 
                                          Collection<? super T> dest) {
        for(T n : src){
            dest.add(n);
        }
    }

4

Ini adalah cara yang paling jelas, paling sederhana bagi saya untuk berpikir tentang ext vs super:

  • extendsuntuk membaca

  • superuntuk menulis

Saya menemukan "Pecs" menjadi cara yang tidak jelas untuk memikirkan hal-hal mengenai siapa yang "produsen" dan siapa yang "konsumen". "PECS" didefinisikan dari perspektif pengumpulan data itu sendiri - koleksi "mengkonsumsi" jika objek sedang ditulis untuk itu (itu memakan objek dari memanggil kode), dan itu "menghasilkan" jika objek sedang dibaca dari itu (itu memproduksi objek ke beberapa kode panggilan). Ini bertentangan dengan bagaimana segala sesuatu disebut. API Java standar dinamai dari perspektif kode panggilan, bukan koleksi itu sendiri. Misalnya, tampilan kumpulan-sentris dari java.util.List harus memiliki metode bernama "accept ()" alih-alih "add ()" - setelah semua,menerima elemen.

Saya pikir itu lebih intuitif, alami dan konsisten untuk memikirkan hal-hal dari perspektif kode yang berinteraksi dengan koleksi - apakah kode "membaca dari" atau "menulis ke" koleksi? Setelah itu, setiap penulisan kode ke koleksi akan menjadi "produsen", dan setiap pembacaan kode dari koleksi akan menjadi "konsumen".


Aku telah lari ke yang tabrakan mental yang sama dan akan cenderung setuju kecuali bahwa Pecs tidak menentukan penamaan kode dan jenis batas-batas sendiri yang ditetapkan pada deklarasi Collection. Selain itu menyangkut penamaan Anda sering memiliki nama untuk memproduksi / mengkonsumsi Koleksi seperti srcdan dst. Jadi Anda berurusan dengan kode dan kontainer pada saat yang sama dan saya akhirnya memikirkannya di sepanjang garis - "kode konsumsi" mengkonsumsi dari wadah produksi, dan "memproduksi kode" memproduksi untuk wadah konsumsi.
mouselabs

4

"Aturan" PECS hanya memastikan bahwa yang berikut ini legal:

  • Konsumen: apa pun ?itu, dapat merujuk secara legal T
  • Produser: apa pun ?itu, secara hukum dapat dirujuk oleh T

Pasangan tipikal sepanjang garis List<? extends T> producer, List<? super T> consumerhanya memastikan bahwa kompiler dapat menegakkan aturan hubungan warisan "IS-A" standar. Jika kita dapat melakukannya secara legal, mungkin lebih mudah untuk mengatakan <T extends ?>, <? extends T>(atau lebih baik lagi di Scala, seperti yang Anda lihat di atas, itu [-T], [+T]. Sayangnya yang terbaik yang bisa kita lakukan adalah <? super T>, <? extends T>.

Ketika saya pertama kali menemukan ini dan memecahnya di kepala saya mekanika masuk akal tetapi kode itu sendiri terus terlihat membingungkan bagi saya - saya terus berpikir "sepertinya batas tidak perlu terbalik seperti itu" - meskipun saya sudah jelas di atas - bahwa itu hanya tentang jaminan kepatuhan dengan aturan referensi standar.

Apa yang membantu saya melihatnya menggunakan tugas biasa sebagai analogi.

Pertimbangkan kode mainan berikut (belum siap produksi):

// copies the elements of 'producer' into 'consumer'
static <T> void copy(List<? extends T> producer, List<? super T> consumer) {
   for(T t : producer)
       consumer.add(t);
}

Menggambarkan ini dalam hal analogi tugas, untuk consumeryang ?wildcard (jenis yang tidak diketahui) adalah referensi - "sisi kiri" dari tugas - dan <? super T>memastikan bahwa apapun ?adalah, T"IS-A"? - yang Tdapat ditugaskan untuk itu, karena ?adalah tipe super (atau paling banyak tipe yang sama) dengan T.

Untuk producerperhatian adalah sama hanya saja terbalik: producer's ?wildcard (tidak diketahui jenis) adalah rujukan - 'sisi kanan' dari tugas - dan <? extends T>memastikan bahwa apapun ?adalah, ?'IS-A' T- yang itu dapat ditugaskan keT , karena ?merupakan jenis sub (atau setidaknya jenis yang sama) dengan T.


2

Ingat ini:

Konsumen makan malam (super); Produser memperluas pabrik orang tuanya


1

Menggunakan contoh kehidupan nyata (dengan beberapa penyederhanaan):

  1. Bayangkan sebuah kereta barang dengan mobil barang sebagai analogi dari daftar.
  2. Anda dapat memasukkan barang ke dalam mobil barang jika ukuran barangnya sama atau lebih kecil dari mobil barang =<? super FreightCarSize>
  3. Anda dapat menurunkan muatan dari gerbong barang jika Anda memiliki tempat yang cukup (lebih dari ukuran muatan) di gudang Anda =<? extends DepotSize>

1

Kovarian : terima subtipe
Kontravarians : terima supertipe

Tipe kovarian hanya baca, sedangkan tipe contravarian hanya menulis.


0

Mari kita lihat contohnya

public class A { }
//B is A
public class B extends A { }
//C is A
public class C extends A { }

Generik memungkinkan Anda untuk bekerja dengan Jenis secara dinamis dengan cara yang aman

//ListA
List<A> listA = new ArrayList<A>();

//add
listA.add(new A());
listA.add(new B());
listA.add(new C());

//get
A a0 = listA.get(0);
A a1 = listA.get(1);
A a2 = listA.get(2);
//ListB
List<B> listB = new ArrayList<B>();

//add
listB.add(new B());

//get
B b0 = listB.get(0);

Masalah

Karena Java's Collection adalah tipe referensi, kami memiliki masalah berikut:

Masalah # 1

//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;

* Generik Swift tidak memiliki masalah seperti itu karena Koleksi adalah Value type[Tentang] oleh karena itu koleksi baru dibuat

Masalah # 2

//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;

Solusinya - Generik Wildcard

Wildcard adalah fitur tipe referensi dan tidak dapat dipakai secara langsung

Solusi # 1 <? super A> alias batas bawah alias contravariance alias konsumen menjamin bahwa itu dioperasikan oleh A dan semua superclasses, oleh karena itu aman untuk ditambahkan

List<? super A> listSuperA;
listSuperA = listA;
listSuperA = new ArrayList<Object>();

//add
listSuperA.add(new A());
listSuperA.add(new B());

//get
Object o0 = listSuperA.get(0);

Solusi # 2

<? extends A>alias batas atas alias kovarian alias produsen menjamin bahwa itu dioperasikan oleh A dan semua subclass, itu sebabnya aman untuk mendapatkan dan melemparkan

List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;

//get
A a0 = listExtendsA.get(0);

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.