سؤال

جئت عبر PECS (قصيرة ل منتج extends والمستهلك super) أثناء القراءة على الأدوية الجيلية.

هل يمكن لأحد أن يشرح لي كيفية استخدام PECS لحل الالتباس بين extends و super?

هل كانت مفيدة؟

المحلول

TL ؛ DR: "PECS" من وجهة نظر المجموعة. إذا كنت كذلك فقط سحب العناصر من مجموعة عامة ، إنه منتج ويجب استخدامه extends; ؛ إذا كنت كذلك فقط حشو العناصر في ، إنه مستهلك ويجب استخدامه super. إذا قمت بذلك مع نفس المجموعة ، يجب ألا تستخدم أيضًا extends أو super.


لنفترض أن لديك طريقة تأخذ كمعلمة مجموعة من الأشياء ، لكنك تريد أن تكون أكثر مرونة من مجرد قبول أ Collection<Thing>.

الحالة 1: تريد أن تمر عبر المجموعة وتفعل الأشياء مع كل عنصر.
ثم القائمة هي منتج, ، لذلك يجب عليك استخدام أ Collection<? extends Thing>.

المنطق هو أن أ Collection<? extends Thing> يمكن أن يحمل أي نوع فرعي من Thing, ، وبالتالي سيتصرف كل عنصر ك Thing عند إجراء عمليتك. (لا يمكنك في الواقع إضافة أي شيء إلى Collection<? extends Thing>, ، لأنك لا تستطيع أن تعرف في وقت التشغيل محدد نوع فرعي من Thing المجموعة تحمل.)

الحالة 2: تريد إضافة أشياء إلى المجموعة.
ثم القائمة هي مستهلك, ، لذلك يجب عليك استخدام أ Collection<? super Thing>.

المنطق هنا هو أنه على عكس Collection<? extends Thing>, Collection<? super Thing> يمكن دائمًا عقد ملف Thing بغض النظر عن نوع المعلمة الفعلي. هنا لا تهتم بما هو موجود بالفعل في القائمة طالما أنه سيسمح Thing لتضاف؛ هذا هو ما ? super Thing ضمانات.

نصائح أخرى

تسمى المبادئ وراء ذلك في علوم الكمبيوتر

  • التباين: ? extends MyClass,
  • المتنقلة: ? super MyClass و
  • الثبات/عدم التباين: MyClass

يجب أن تشرح الصورة أدناه المفهوم. الصورة مجاملة: أندريه توكين

Covariance vs Contravariance

PECS (المنتج extends والمستهلك super)

ذاكري → احصل ووضع مبدأ.

ينص هذا المبدأ على ما يلي:

  • استخدم بطاقة Wildcard عندما تحصل على قيم فقط من الهيكل.
  • استخدم بطاقة Wildcard الفائقة عندما تضع القيم فقط في هيكل.
  • ولا تستخدم بطاقة Wildcard عندما تحصل عليها وتضعها.

مثال في جافا:

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: إذا كان S نوعًا فرعيًا من T ، فقد يتم استبدال كائنات T من النوع T بكائنات من النوع S.

ضمن نظام لغة البرمجة ، قاعدة الكتابة

  • تشترك إذا كانت تحافظ على ترتيب الأنواع (≤) ، والتي تطلب الأنواع من أكثر تحديداً إلى أكثر عاما ؛
  • مخالفة إذا كان يعكس هذا الطلب ؛
  • ثابت أو غير المتغير إذا لم ينطبق أي من هذه.

التباين والتناقض

  • يمكن أن تكون أنواع البيانات القراءة فقط (مصادر) تشترك;
  • يمكن أن تكون أنواع البيانات للكتابة فقط (المصارف) مخالفة.
  • يجب أن تكون أنواع البيانات القابلة للتغيير التي تعمل كمصادر ومصارف ثابت.

لتوضيح هذه الظاهرة العامة ، فكر في نوع الصفيف. بالنسبة للحيوان النوع ، يمكننا أن نجعل نوع الحيوان [

  • تشترك: القط [] هو حيوان [] ؛
  • مخالفة: حيوان [] هو قطة [] ؛
  • ثابت: حيوان [] ليس قطة [] والقط [] ليس حيوانًا [].

أمثلة جافا:

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  

مزيد من الأمثلة

المحصورة(أي متجه نحو مكان ما) البرية : هناك 3 نكهات مختلفة من البرية:

  • في التصنيف/عدم التباين: ? أو ? extends Object - غير محدود البرية. إنه يقف للعائلة من جميع الأنواع. استخدم عندما تحصل على كلاهما وتضعه.
  • التباين المشارك: ? extends T (الأسرة من جميع الأنواع التي هي أنواع فرعية من T) - بطاقة برية مع الحد الاعلى. T هل العلوي-معظم الطبقة في التسلسل الهرمي الميراث. استخدم extends بطاقة Wildcard عندما أنت فقط احصل على القيم من الهيكل.
  • التنافس: ? super T (الأسرة من جميع الأنواع التي هي supertypes من T) - بطاقة برية مع أ الأدنى. T هل أدنى-معظم الطبقة في التسلسل الهرمي الميراث. إستخدم super بطاقة Wildcard عندما أنت فقط وضع القيم في هيكل.

ملاحظة: Wildcard ? يعني صفر أو مرة واحدة, ، يمثل نوع غير معروف. يمكن استخدام البرية كنوع من المعلمة ، ولم يتم استخدامها مطلقًا كوسيطة نوع لاستدعاء طريقة عامة ، إنشاء مثيل من الفئة العامة. (أي عند استخدام البدل البري الذي لم يتم استخدامه في أي مكان آخر في البرنامج كما نستخدمه T)

enter image description here

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

الأدوية الجيرية و أمثلة

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

كما أشرح في إجابتي لسؤال آخر ، PECS هو جهاز ذاكري تم إنشاؤه بواسطة Josh Bloch للمساعدة في التذكر صرودوكر هxtends, جonsumer سuper.

هذا يعني أنه عندما يتم تمرير نوع معلمة إلى طريقة ما ينتج حالات T (سيتم استردادهم منه بطريقة ما) ، ? extends T يجب استخدامه ، لأن أي مثيل من فئة فرعية من T هو أيضا T.

عندما يتم تمرير نوع معلمة يتم نقله إلى طريقة ما تستهلك حالات T (سيتم نقلهم إلى ذلك لفعل شيء ما) ، ? super T يجب استخدامه لأن مثيل T يمكن تمريرها قانونًا إلى أي طريقة تقبل بعضًا من النوع الخارق T. أ Comparator<Number> يمكن استخدامه على أ Collection<Integer>, ، فمثلا. ? extends T لن يعمل ، لأن أ Comparator<Integer> لا يمكن أن تعمل على أ Collection<Number>.

لاحظ أنه يجب عليك استخدامك بشكل عام فقط ? extends T و ? super T لمعلمات بعض الطرق. يجب استخدام الأساليب فقط T كمعلمة النوع على نوع العودة العامة.

باختصار ، ثلاث قواعد سهلة لتذكر PECS:

  1. استخدم ال <? extends T> Wildcard إذا كنت بحاجة إلى استرداد كائن من النوع T من مجموعة.
  2. استخدم ال <? super T> Wildcard إذا كنت بحاجة إلى وضع كائنات من النوع T في مجموعة.
  3. إذا كنت بحاجة إلى إرضاء كلا الأمرين ، فلا تستخدم أي بطاقة Wildcard. بهذه البساطة.

(إضافة إجابة لأن أمثلة كافية مع الأدوات البرية العامة)

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

لنفترض هذا التسلسل الهرمي:

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 - تمتد المنتج:

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

لماذا لا يمكنك إضافة كائنات تمتد "سمك القرش" في هذه القائمة؟ مثل:

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

نظرًا لأن لديك قائمة يمكن أن تكون من النوع A أو B أو C في وقت التشغيل, ، لا يمكنك إضافة أي كائن من النوع A أو B أو C فيه لأنه يمكن أن ينتهي بك الأمر بمجموعة غير مسموح بها في Java.
في الممارسة العملية ، يمكن للمترجم أن يرى بالفعل في التجميع أن تضيف B:

sharks.add(new HammerShark());

... ولكن ليس لديها طريقة لمعرفة ما إذا كان في وقت التشغيل ، سيكون B الخاص بك من النوع الفرعي أو supertype من نوع القائمة. في وقت التشغيل ، يمكن أن يكون نوع القائمة أيًا من الأنواع A ، B ، C. لذلك لا يمكنك إضافة Hammerskark (النوع الفائق) في قائمة Deadhammershark على سبيل المثال.

*ستقول: "حسنًا ، ولكن لماذا لا يمكنني إضافة Hammerskark فيه لأنه أصغر نوع؟". الجواب: إنه الأصغر أنت أعرف. ولكن يمكن تمديد Hammerskark أيضًا من قبل شخص آخر وينتهي بك الأمر في نفس السيناريو.

دعنا نوضح CS - المستهلك Super:

في نفس التسلسل الهرمي ، يمكننا تجربة هذا:

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

ماذا ولماذا أنت يستطيع أضف إلى هذه القائمة؟

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

يمكنك إضافة الأنواع المذكورة أعلاه من الكائنات لأن أي شيء أسفل سمك القرش (A ، B ، C) سيكون دائمًا أنواعًا فرعية لأي شيء فوق سمك القرش (X ، Y ، Z). سهل الفهم.

أنت لا تستطيع أضف أنواعًا فوق سمك القرش ، لأن في وقت التشغيل يمكن أن يكون نوع الكائن المضافة أعلى في التسلسل الهرمي من النوع المعلن للقائمة (x ، y ، z). هذا غير مسموح به.

ولكن لماذا لا يمكنك القراءة من هذه القائمة؟ (أعني أنه يمكنك الحصول على عنصر منه ، لكن لا يمكنك تعيينه إلى أي شيء آخر غير الكائن O):

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

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

في وقت التشغيل ، يمكن أن يكون نوع القائمة أي نوع أعلاه A: x ، y ، z ، ... يمكن للمترجم تجميع بيان المهمة الخاص بك (والذي يبدو صحيحًا) ولكن ، ، ولكن ، في وقت التشغيل يمكن أن يكون نوع S (حيوان) أقل في التسلسل الهرمي من النوع المعلن من القائمة (والذي يمكن أن يكون مخلوقًا ، أو أعلى). هذا غير مسموح به.

لتلخيص

نحن نستخدم <? super T> لإضافة كائنات من الأنواع متساوية أو أقل في القائمة. لا يمكننا القراءة منه.
نحن نستخدم <? extends T> لقراءة كائنات الأنواع متساوية أو أقل من القائمة. لا يمكننا إضافة عنصر إليه.

تذكر هذا:

المستهلك يأكل عشاء(ممتاز)؛ منتج يمتد مصنع والديه

التباين: قبول الأنواع الفرعية
المتنقلة: قبول supertypes

أنواع المتغيرات القراءة فقط ، في حين أن الأنواع المتناقضة هي الكتابة فقط.

استخدام مثال الحياة الحقيقية (مع بعض التبسيط):

  1. تخيل قطار الشحن مع سيارات الشحن كقياس لقائمة.
  2. تستطيع وضع شحنة في سيارة شحن إذا كانت الشحنة لديها نفس الحجم أو أصغر من سيارة الشحن = <? super FreightCarSize>
  3. تستطيع تفريغ شحنة من سيارة شحن إذا كان لديك مكان كاف (أكثر من حجم الشحن) في مستودعك = <? extends DepotSize>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top