Java >> Java tutorial >  >> Tag >> extends

Hvad er PECS (Producer Extends Consumer Super)?

tl;dr: "PECS" er fra samlingens synspunkt. Hvis du kun er trækker varer fra en generisk samling, er det en producent, og du skal bruge extends; hvis du kun er propper varer i, er det en forbruger, og du skal bruge super . Hvis du gør begge dele med den samme samling, bør du hverken bruge extends eller super .

Antag, at du har en metode, der tager en samling ting som parameter, men du vil have, at den skal være mere fleksibel end blot at acceptere en Collection<Thing> .

Case 1:Du vil gennemgå samlingen og gøre ting med hvert enkelt element.
Så er listen en producent , så du skal bruge en Collection<? extends Thing> .

Begrundelsen er, at en Collection<? extends Thing> kunne indeholde enhver undertype af Thing , og dermed vil hvert element opføre sig som en Thing når du udfører din operation. (Du kan faktisk ikke tilføje noget (undtagen null) til en Collection<? extends Thing> , fordi du ikke ved kørsel kan vide hvilken specifik undertype af Thing samlingen holder.)

Tilfælde 2:Du vil føje ting til samlingen.
Så er listen en forbruger , så du skal bruge en Collection<? super Thing> .

Begrundelsen her er, at i modsætning til Collection<? extends Thing> , Collection<? super Thing> kan altid indeholde en Thing uanset hvad den faktiske parametrerede type er. Her er du ligeglad med, hvad der allerede er på listen, så længe det tillader en Thing skal tilføjes; dette er hvad ? super Thing garantier.


Principperne bag dette i datalogi kaldes

  • Kovarians:? extends MyClass ,
  • Kontravarians:? super MyClass og
  • Invarians/ikke-varians:MyClass

Billedet nedenfor skal forklare konceptet. Billede høflighed:Andrey Tyukin


PECS (Producer extends og forbruger super )

mnemonic → Hent og læg princippet .

Dette princip anfører, at:

  • Brug en extends jokertegn, når du kun får værdier ud af en struktur.
  • Brug en super jokertegn, når du kun sætter værdier ind i en struktur.
  • Og brug ikke et jokertegn, når du både henter og putter.

Eksempel i Java:

class Super {
        Number testCoVariance() {
            return null;
        }
        void testContraVariance(Number parameter) {
        } 
    }
    
    class Sub extends Super {
        @Override
        Integer testCoVariance() {
            return null;
        } //compiles successfully i.e. return type is don't care(Integer is subtype of Number)
        @Override
        void testContraVariance(Integer parameter) {
        } //doesn't support even though Integer is subtype of Number
    }

Liskov Substitution Principle (LSP) siger, at "objekter i et program bør kunne udskiftes med forekomster af deres undertyper uden at ændre det pågældende programs korrekthed ”.

Inden for typesystemet i et programmeringssprog, en skriveregel

  • covariant hvis den bevarer rækkefølgen af ​​typer (≤), hvilket sorterer typer fra mere specifikke til mere generiske;
  • kontravariant hvis det omvendte denne rækkefølge;
  • invariant eller ikke-variant, hvis ingen af ​​disse gælder.

Kovarians og kontravarians

  • Skrivebeskyttede datatyper (kilder) kan være samvariante;
  • skrivbare datatyper (sinks) kan være kontravariante .
  • Foranderlige datatyper, der fungerer som både kilder og synker, bør være invariante .

For at illustrere dette generelle fænomen skal du overveje array-typen. For typen Dyr kan vi lave typen Dyr[]

  • covariant :en kat[] er et dyr[];
  • kontravariant :et dyr[] er en kat[];
  • invariant :et dyr[] er ikke en kat[] og en kat[] er ikke et dyr[].

Java-eksempler:

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  

flere eksempler

Billedkilde

afgrænset (dvs. på vej mod et sted) jokertegn :Der er 3 forskellige varianter af jokertegn:

  • In-varians/Ikke-varians:? eller ? extends Object - Ubegrænset Wildcard. Det står for familien af ​​alle typer. Brug når du både får og putter.
  • Kovarians:? extends T (familien af ​​alle typer, der er undertyper af T ) - et jokertegn med en øvre grænse . T er den øverste -mest klasse i arvehierarkiet. Brug en extends jokertegn, når du kun værdier ud af en struktur.
  • Kontravarians:? super T (familien af ​​alle typer, der er supertyper af T ) - et jokertegn med en nedre grænse . T er den nedre -mest klasse i arvehierarkiet. Brug en super jokertegn, når du kun Put værdier ind i en struktur.

Bemærk:jokertegn ? betyder nul eller én gang , repræsenterer en ukendt type. Jokertegnet kan bruges som typen af ​​en parameter, aldrig brugt som et type-argument for en generisk metodeopkald, en generisk klasseforekomstoprettelse. (dvs. når det bruges jokertegn, den reference, der ikke bruges andre steder i programmet, som vi bruger T )

 import java.util.ArrayList;
import java.util.List;

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 {

    public static void main(String[] args) {
        //? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
        List<? extends Shape> intList5 = new ArrayList<Shape>();
        List<? extends Shape> intList6 = new ArrayList<Cricle>();
        List<? extends Shape> intList7 = new ArrayList<Rectangle>();
        List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.


        //? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
        List<? super Shape> inList5 = new ArrayList<Shape>();
        List<? super Shape> inList6 = new ArrayList<Object>();
        List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.

        //-----------------------------------------------------------
        Circle circle = new Circle();
        Shape shape = circle; // OK. Circle IS-A Shape

        List<Circle> circles = new ArrayList<>();
        List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape

        List<? extends Circle> circles2 = new ArrayList<>();
        List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>


        //-----------------------------------------------------------
        Shape shape2 = new Shape();
        Circle circle2= (Circle) shape2; // OK. with type casting

        List<Shape> shapes3 = new ArrayList<>();
        List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of  List<Shape> even Circle is subetype of Shape

        List<? super Shape> shapes4 = new ArrayList<>();
        List<? super Circle> circles4 = shapes4; //OK.
    }

    
    
    /*
     * Example for an upper bound wildcard (Get values i.e Producer `extends`)
     *
     * */
    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape()); //ERROR
        list.add(new Circle()); // ERROR
        list.add(new Square()); // ERROR
        list.add(new Rectangle()); // ERROR
        Shape shape= list.get(0);//OK 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 Object());//ERROR
        list.add(new Shape());//OK
        list.add(new Circle());//OK
        list.add(new Square());//OK
        list.add(new Rectangle());//OK
        Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
        Object object= list.get(0); //OK 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.
         */
    }
}

generiske artikler og eksempler

Kovarians og kontravarians bestemmer kompatibilitet baseret på typer. I begge tilfælde er varians en rettet relation. Kovarians kan oversættes til "forskellige i samme retning ," eller med-forskellig, hvorimod kontravarians betyder "forskellig i den modsatte retning ," eller mod-forskel. Kovariante og kontravariante typer er ikke de samme, men der er en sammenhæng mellem dem. Navnene angiver retningen af ​​korrelationen.


Java tag