PECS (Üretici Tüketici Süper'yi Genişletir) nedir?


730

Jenerik okurken PECS ( Üretici extendsve Tüketicisuper kısaltması ) ile karşılaştım.

Birisi bana extendsve arasındaki karışıklığı çözmek için PECS nasıl kullanılacağını açıklayabilir supermi?


3
@ Youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 örneğini açıklayan superancak başka bir fikir veren çok iyi bir açıklama .
lupchiazoem

Yanıtlar:


845

tl; dr: "PECS" koleksiyonun bakış açısından. Eğer varsa sadece genel bir koleksiyondan öğe çekerek, bu bir üreticidir ve kullanmak gerekir extends; Eğer varsa sadece öğeleri doldurma, bir tüketici olduğunu ve kullanmalıdır super. Eğer ikisi de aynı koleksiyonu ile yaparsanız, ya kullanmamalısınız extendsya super.


Parametre olarak bir şeyler koleksiyonu alan bir yönteminiz olduğunu, ancak yalnızca a'yı kabul etmekten daha esnek olmasını istediğinizi varsayalım Collection<Thing>.

Durum 1: Koleksiyondan geçmek ve her bir öğe ile bir şeyler yapmak istiyorsunuz.
O zaman liste bir yapımcıdır , bu yüzden bir kullanmalısınız Collection<? extends Thing>.

Bunun nedeni, Collection<? extends Thing>a'nın herhangi bir alt türüne sahip olabilmesidir Thingve bu nedenle her bir öğe, Thingişleminizi gerçekleştirirken bir gibi davranacaktır . (Aslında bir şey ekleyemezsiniz Collection<? extends Thing>çalışma zamanında bilemez çünkü, belirli bir alt tipi Thingkoleksiyonunda tutar.)

Durum 2: Koleksiyona bir şeyler eklemek istiyorsunuz.
O zaman liste bir tüketicidir , bu yüzden bir Collection<? super Thing>.

Burada akıl aksine olmasıdır Collection<? extends Thing>, Collection<? super Thing>her zaman tutabilir Thingolursa olsun fiili parametreli tür olduğunu. Burada, a'nın Thingeklenmesine izin verdiği sürece listede nelerin bulunduğunu umursamazsınız ; bunu ? super Thinggaranti eder.


142
Her zaman bu şekilde düşünmeye çalışıyorum: Bir üreticinin daha spesifik bir şey üretmesine izin verilir, bu nedenle genişler , bir tüketicinin daha genel bir şey kabul etmesine izin verilir, bu nedenle süper .
Feuermurmel

10
Üretici / tüketici ayrımını hatırlamanın bir başka yolu da bir yöntem imzası düşünmektir. Bir yönteminiz doSomethingWithList(List list)varsa , listeyi tüketiyorsunuzdur ve bu nedenle kovaryans / genişletmeler (veya değişmez bir Liste) gerekir. Öte yandan yönteminiz ise List doSomethingProvidingList, o zaman Liste üretiyorsunuzdur ve karşıtlık / süper (ya da değişmez bir Liste) gerekecektir.
Raman

3
@MichaelMyers: Neden bu iki durum için de parametreli bir türü kullanamıyoruz? Burada joker karakterler kullanmanın herhangi bir avantajı var mı, yoksa yalnızca constyöntemin argümanları değiştirmediğini belirtmek için C ++ 'da yöntem parametreleri olarak başvuruları kullanmaya benzer şekilde okunabilirliği geliştirmenin bir yolu var mı?
Chatterjee

7
@Raman, bence biraz kafa karıştırdın. DoSthWithList'te (<<super Thing> Listesine sahip olabilirsiniz), bir tüketici olduğunuz için super kullanabilirsiniz (unutmayın, CS). Ancak, Liste <? (PE) üretirken daha spesifik bir şey döndürmenize izin verildiğinden Thing> getList () öğesini genişletir.
masterxilo

4
@AZ_ Duygularınızı paylaşıyorum. Bir yöntem listeden get () yaparsa, yöntem Tüketici <T> olarak kabul edilir ve liste sağlayıcı olarak kabul edilir; ancak PECS'in kuralı “listenin bakış açısından” dır, dolayısıyla 'uzanır' olarak adlandırılır. GEPS olmalı: uzanır; süper koy.
Treefish Zhang

563

Bilgisayar biliminde bunun arkasında yatan ilkelere

  • Kovaryans: ? extends MyClass,
  • Çelişki: ? super MyClassve
  • Değişmezliği / olmayan varyans: MyClass

Aşağıdaki resim kavramı açıklamalıdır. Resim nezaket: Andrey Tyukin

Kovaryans ve Karşıtlık


144
Selam millet. Ben Andrey Tyukin, sadece anoopelias & DaoWen'in benimle iletişime geçtiğini ve çizimi kullanma iznimi aldığını doğrulamak istedim, (CC) -BY-SA lisansı altında. Thx @ Anoop ikinci bir hayat verdiği için ^^ @Brian Agnew: ("birkaç oy"): Bunun nedeni Scala için bir taslak olması, Scala sözdizimini kullanması ve Java'nın tuhaf çağrısından oldukça farklı olan bildirim sitesi varyansı varsaymasıdır. -site varyansı ... Belki de bu
çizimin

3
Bu, şimdiye kadar bulduğum Covariance ve Contravariance için en basit ve en açık açıklamalardan biridir!
cs4r

@Andrey Tyukin Merhaba, ben de bu resmi kullanmak istiyorum. Sizinle nasıl irtibat kurabilirim?
slouc

Bu örnek hakkında herhangi bir sorunuz varsa, bunları sohbet odasında tartışabiliriz: chat.stackoverflow.com/rooms/145734/…
Andrey Tyukin


49

PECS (Üretici extendsve Tüketici super)

anımsatıcı → Al ve koy ilkesi.

Bu ilke şunları ifade eder:

  • Yalnızca bir yapıdan değerler aldığınızda, genişletilmiş bir joker karakter kullanın.
  • Bir yapıya yalnızca değerler koyarken bir süper joker karakter kullanın.
  • Hem alırken hem de koyarken joker karakter kullanmayın.

Java örneği:

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

}

Liskov ikame prensibi: S, T'nin bir alt tipiyse, T tipi nesnelerin yerine S tipi nesneler kullanılabilir.

Bir programlama dilinin tip sistemi içinde bir yazma kuralı

  • daha spesifik olandan daha genel olana kadar olanları sıralayan türlerin (≤) sırasını koruyorsa kovaryant ;
  • bu sıralamayı tersine çevirirse çelişkili ;
  • bunların hiçbiri geçerli değilse değişmez veya değişken olmayan.

Kovaryans ve karşıtlık

  • Salt okunur veri türleri (kaynaklar) kovaryant olabilir ;
  • salt yazılır veri türleri (lavabolar) karşıt değişken olabilir .
  • Hem kaynak hem de lavabo işlevi gören değişken veri türleri değişmez olmalıdır .

Bu genel durumu göstermek için dizi türünü göz önünde bulundurun. Hayvan türü için Hayvan türü yapabiliriz []

  • covariant : Bir Kedi [] bir Hayvan [];
  • kontravaryant : bir Hayvan [] bir Kedi [];
  • değişmez : Bir Hayvan [] bir Kedi [] ve Bir Kedi [] bir Hayvan [] değildir.

Java Örnekleri:

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  

daha fazla örnek

sınırlı (yani bir yere doğru ilerleyen) joker karakter : 3 farklı joker karakter çeşidi vardır:

  • Varyans / Varyans Olmayan: ?veya ? extends Object- Sınırsız Joker Karakter. Her türlü ailenin kısaltmasıdır. Hem alıp koyduğunuzda kullanın.
  • Eş-varyans: ? extends T(alt tipleri olan her türden aile T) - üst sınırı olan bir joker karakter . Tolan üst kalıtım hiyerarşisinde -En sınıfı. Bir kullan extendsyalnızca zaman joker alın bir yapının dışına değerlerini.
  • Kontra-varyans: ? super T(süper tipleri olan her türden aile T) - alt sınırı olan bir joker karakter . Tolduğu alt kalıtım hiyerarşisinde -En sınıfı. Bir kullan superyalnızca zaman joker koyun bir yapıya değerleri.

Not: joker karakter sıfır veya bir kez? anlamına gelir , bilinmeyen bir türü temsil eder. Joker genel bir yöntem çağırma, genel bir sınıfı örneği oluşturulması için bir tip değişken olarak kullanılan hiç bir parametrenin türü olarak kullanılabilir. (Yani kullandığımız benzer referans programında başka bir yerde kullanılan değil ikinci Joker )T

resim açıklamasını buraya girin

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.
        */  
    }
}

jenerikler ve örnekler


Hey, sadece son cümle ile ne demek istediğini bilmek istedim: "Eğer benzetimin yanlış olduğunu düşünüyorsanız lütfen güncelleyin". Etik olarak yanlış (öznel olan) mı yoksa programlama bağlamında yanlış mı (nesnel: hayır, yanlış değil) demek istiyor musunuz? Bunu, kültürel normlardan ve etik inançlardan bağımsız olarak evrensel olarak kabul edilebilir daha tarafsız bir örnekle değiştirmek istiyorum; Bu senin için uygunsa.
Neuron

sonunda alabilirim. Güzel açıklama.
Oleg Kuts

2
@Premraj,, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.<?> Veya <Liste'ye öğe ekleyemiyorum? Object> öğesini genişletir, bu yüzden neden olabileceğini anlamıyorum Use when you both get and put.
LiuWenbin_NO.

1
@LiuWenbin_NO. - Cevabın bu kısmı yanıltıcı. ?- "sınırsız joker karakter" - değişmezliğin tam tersine karşılık gelir. Lütfen aşağıdaki belgelere bakın: docs.oracle.com/javase/tutorial/java/generics/… belirten: Kodun değişkene hem "giriş" hem de "çıkış" değişkeni olarak erişmesi gerektiğinde, joker karakter kullanmayın. ("Get" ve "put" ile eşanlamlı olarak "in" ve "out" ifadelerini kullanıyorlar). Dışında nullbir Koleksiyonu ile parametrelendirmek size ekleyemezsiniz ?.
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
    }
}

Yani "? B uzanır", "? B uzanır" olarak yorumlanmalıdır. Bu, B'nin genişlettiği bir şeydir, böylece B'nin kendisi hariç, B'nin tüm süper sınıflarını Nesneye kadar içerir. Kod için teşekkürler!
Saurabh Patil

3
@SaurabhPatil No, ? extends BB anlamına gelir ve B'yi genişleten herhangi bir şey
28:16

24

Ben açıklamak üzere cevabım başka soruya, PECS yardımına Josh Bloch tarafından oluşturulan bir anımsatıcı cihaz hatırlamak olduğunu P roducer extends, C onsumer super.

Bu, bir yönteme geçirilen parametreli bir türün , bir alt sınıfının herhangi bir örneği de a olduğu için (bir şekilde ondan alınacak) örnekleri üreteceği anlamına gelir .T? extends TTT

Bir yönteme geçirilen parametreli bir türün (bir şey yapmak için kendisine aktarılacak) örneklerini tüketeceği zaman kullanılmalıdır, çünkü bir örneği, bazı üst türlerini kabul eden herhangi bir yönteme yasal olarak geçirilebilir . Bir bir ile kullanılabilir , örneğin. işe yaramaz, çünkü a .T? super TTTComparator<Number>Collection<Integer>? extends TComparator<Integer>Collection<Number>

Genel olarak yalnızca ? extends Tve ? super Tbazı yöntemlerin parametrelerini kullanmanız gerektiğini unutmayın . Yöntemler yalnızca Tgenel bir dönüş türünde type parametresi olarak kullanılmalıdır .


1
Bu ilke sadece Koleksiyonlar için mi geçerli? Kişi listeyle ilişkilendirmeye çalıştığında anlamlıdır. Eğer sıralama imzasını düşünürseniz (Liste <T>, Karşılaştırıcı <? Super T>) ---> Burada Karşılaştırıcı süper kullanır, bu nedenle PECS bağlamında bir tüketici olduğu anlamına gelir. Örneğin uygulamaya baktığınızda: public int compar (Kişi a, Kişi b) {return a.age <b.age? -1: a.age == b.age? 0: 1; } Kişi yaştan başka bir şey tüketmiyor gibi hissediyorum. Bu beni karıştırıyor. Akıl yürütmemde bir kusur var mı veya PECS sadece Koleksiyonlar için mi geçerli?
Fatih Arslan

24

Özetle, PECS'yi hatırlamak için üç kolay kural:

  1. <? extends T>Tür nesnesini almanız gerekiyorsa joker karakteri kullanınTBir koleksiyondan .
  2. <? super T>Türdeki nesneleri koymanız gerekiyorsa joker karakteri kullanınTBir koleksiyona .
  3. Her ikisini de tatmin etmeniz gerekiyorsa, herhangi bir joker karakter kullanmayın. Kadar basit.

10

bu hiyerarşiyi varsayalım:

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

PE'yi açıklayalım - Üretici Kapsamı:

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

Neden bu listeye "Köpekbalığı" nı genişleten nesneler ekleyemiyorsunuz? sevmek:

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

Çalışma zamanında A, B veya C türünde olabilen bir listeniz olduğundan, java'da izin verilmeyen bir kombinasyon oluşturabileceğiniz için A, B veya C türünde herhangi bir nesne ekleyemezsiniz.
Pratikte, derleyici gerçekten derleme sırasında B eklediğinizi görebilir:

sharks.add(new HammerShark());

... ancak çalışma zamanında B'nizin liste türünün bir alt türü veya süper türü olup olmadığını anlamanın bir yolu yoktur. Çalışma zamanında liste türü A, B, C türlerinden herhangi biri olabilir. Böylece örneğin DeadHammerShark listesine HammerSkark (süper tip) eklemeniz mümkün değildir.

* "Tamam, ama neden en küçük tip olduğu için HammerSkark'ı ekleyemiyorum?" Cevap: Bildiğiniz en küçük şey . Ancak HammerSkark başka biri tarafından da genişletilebilir ve aynı senaryoya girersiniz.

CS - Tüketici Süper:

Aynı hiyerarşide bunu deneyebiliriz:

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

Ne ve neden olabilir bu listeye eklemek?

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

Yukarıdaki nesne türlerini ekleyebilirsiniz, çünkü köpekbalığının altındaki her şey (A, B, C) her zaman köpekbalığının üzerindeki her şeyin alt tipleri olacaktır (X, Y, Z). Anlaması kolay.

Sen olamaz , çünkü Shark yukarıdaki türleri eklemek zamanında eklenen nesnenin türü listesinde (X, Y, Z) belirtilen türüne göre hiyerarşide daha yüksek olabilir. Buna izin verilmiyor.

Ama neden bu listeden okuyamıyorsunuz? (Demek istediğim, ondan bir öğe alabilirsin, ama bunu Object o dışında bir şeye atayamazsın):

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

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

Çalışma zamanında, liste türü A'nın üstündeki herhangi bir tür olabilir: X, Y, Z, ... Derleyici atama ifadenizi (doğru görünüyor) derleyebilir, ancak çalışma zamanında s (Hayvan) türü daha düşük olabilir hiyerarşi listenin beyan edilen türüne göre (Yaratık veya üstü olabilir). Buna izin verilmiyor.

Sonuç olarak

Biz kullanmak <? super T>türdeki nesnelerin eşit veya altında eklemek Tiçin List. Ondan okuyamayız. Listeden eşit veya aşağı tipte nesneleri okumak için
kullanırız . <? extends T>TOna eleman ekleyemeyiz.


9

(bir cevap eklemek, çünkü Generics joker karakterleriyle asla yeterli örnek vermemek)

       // 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

Bu benim için süper genişletir düşünüyorum en açık, en basit yolu:

  • extendsiçindir okuma

  • superiçindir yazı

"PECS" i kimin "üretici" ve kimin "tüketici" olduğu hakkında düşünmenin açık olmayan bir yolu olarak görüyorum. "PECS" veri toplamanın kendisi açısından tanımlanır nesneler yazılıyor eğer toplama "tükettiğini" - için (o çağıran koddan nesneleri tüketen) o, ve nesneler okunduğunu eğer o "üretir" dan (bunu o bazı arama kodlarına nesne üretiyor). Bu, her şeyin nasıl adlandırıldığının tersidir. Standart Java API'leri, koleksiyonun kendisi değil, arama kodunun perspektifinden adlandırılır. Örneğin, java.util.List öğesinin koleksiyon merkezli bir görünümünde "add ()" yerine "take ()" adlı bir yöntem bulunmalıdır - sonuçta,ancak listenin kendisi öğeyi alır .

Ben koleksiyon ile etkileşen kod perspektifinden şeyler düşünmek daha sezgisel, doğal ve tutarlı - kod "okuma" veya "koleksiyon" yazma "mı? Bunu takiben , koleksiyona yazılan herhangi bir kod "üretici" ve koleksiyondan okunan herhangi bir kod "tüketici" olacaktır.


O aynı zihinsel çarpışma yaşamanıza ve PECS kod adlandırılmasını ve kendileri tipi sınırlarını belirtmez dışında kabul etmek eğiliminde olacaktır edilmektedir Koleksiyon beyanlarına ayarlanır. Ayrıca, adlandırma söz konusu olduğunda, srcve gibi Koleksiyonlar üretmek / tüketmek için genellikle adlarınız vardır dst. Yani hem kod hem de kapsayıcılarla aynı anda ilgileniyorsunuz ve bu satırlar üzerinde düşünmeye başladım - "kod tüketmek", üreten bir kaptan tüketiyor ve "kod üretmek", tüketen bir kap için üretiyor.
mouselabs

4

PECS "kuralı" sadece aşağıdakilerin yasal olmasını sağlar:

  • Tüketici: her neyse ?, yasal olarak başvurabilir T
  • Yapımcı: ne olursa olsun ?, bu yasal olarak alınabilir tarafından sevk T

Çizgileri boyunca tipik eşleştirme List<? extends T> producer, List<? super T> consumer, derleyicinin standart "IS-A" kalıtım ilişkisi kurallarını uygulayabilmesini sağlamaktır. Bunu yasal olarak yapabilseydik, daha kolay söyleyebiliriz <T extends ?>, <? extends T>(ya da Scala'da yukarıda gördüğünüz gibi [-T], [+T]daha iyi olabilir) <? super T>, <? extends T>.

Bunu ilk karşılaştığımda ve kafamda bozduğumda, mekanikler mantıklıydı ama kodun kendisi bana kafa karıştırıcı görünmeye devam etti - "sınırların bu şekilde ters çevrilmesi gerekmiyor gibi görünüyor" diye düşündüm - yukarıdakilere açıktı - sadece standart referans kurallarına uyumu garanti etmekle ilgili.

Bana yardımcı olan şey, sıradan ödevi bir benzetme olarak kullanarak bakmaktı.

Aşağıdaki (üretime hazır değil) oyuncak kodunu göz önünde bulundurun:

// 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);
}

İçin, atama benzetme bakımından Bunun açıklanması atamanın "sol taraftaki" - - ve referans joker (bilinmeyen tip) olduğu her şeyin olmasını sağlar olduğunu "-A IS" - buna atanabilir, çünkü olarak bir süper tiptir (veya en fazla aynı tiptir) .consumer?<? super T>?T?T?T

İçin producerendişe sadece ters oluyor aynıdır: producer'ın ?joker (bilinmeyen tip)' dir referent - atama 'sağ tarafını' - ve <? extends T>her ne olmasını sağlar ?olduğunu ?'-A IS' T- yani o atanabilir a kadarT , çünkü ?bir alt türdür (veya en azından aynı tür) T.


2

Hatırla bunu:

Tüketici yemek yemek (süper); Yapımcı ebeveyninin fabrikasını genişletiyor


1

Gerçek hayat örneği kullanma (bazı basitleştirmelerle):

  1. Bir listeye benzeyen yük vagonlarına sahip bir yük treni düşünün.
  2. Sen edebilirsiniz koymak kargo varsa yük vagonunda bir kargo aynı veya daha küçük boyut yük vagonu daha =<? super FreightCarSize>
  3. Sen edebilirsiniz boşaltmak varsa yük vagonunda bir kargo yeterli yer (Daha fazla kargo boyutundan daha çok) = deposunda<? extends DepotSize>

1

Kovaryans : alt tipleri kabul et
Contravariance : üst tipleri kabul et

Kovaryant tipler salt okunurken kontravaryant tipler salt okunurdur.


0

Örnek bir göz atalım

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

Generics, Türlerle dinamik olarak güvenli bir şekilde çalışmanıza olanak tanır

//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);

Sorun

Java'nın Koleksiyonu sonuç olarak bir referans türü olduğundan, sonraki sorunlarımız var:

Sorun # 1

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

* Swift'in jenerikinde böyle bir sorun yok çünkü Koleksiyon Value type[Hakkında] olduğundan yeni bir koleksiyon oluşturuldu

Sorun # 2

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

Çözüm - Genel Joker Karakterler

Joker karakter bir başvuru türü özelliğidir ve doğrudan somutlaştırılamaz

Çözüm # 1 <? super A> aka alt sınır aka karşıtlık aka tüketiciler, A ve tüm üst sınıflar tarafından çalışacağını garanti eder, bu yüzden eklemek güvenlidir

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);

Çözüm # 2

<? extends A>aka üst sınır aka kovaryans aka üreticiler, A ve tüm alt sınıflar tarafından işletildiğini garanti eder, bu yüzden almak ve dökmek güvenlidir

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

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.