CodeGym/Java-blogg/Tilfeldig/Jokertegn i generikk
John Squirrels
Nivå
San Francisco

Jokertegn i generikk

Publisert i gruppen
Hei! La oss fortsette studiet av generiske legemidler. Du har allerede fått en betydelig mengde kunnskap om dem fra tidligere leksjoner (om bruk av varargs når du arbeider med generiske medisiner og om typesletting ), men det er et viktig emne som vi ennå ikke har vurdert – jokertegn . Dette er en veldig viktig egenskap ved generiske legemidler. Så mye at vi har dedikert en egen leksjon til det! Når det er sagt, er det ikke noe spesielt komplisert med jokertegn. Det ser du med en gang :) Jokertegn i generikk - 1La oss se på et eksempel:
public class Main {

   public static void main(String[] args) {

       String str = new String("Test!");
       // No problem
       Object obj = str;

       List<String> strings = new ArrayList<String>();
       // Compilation error!
       List<Object> objects = strings;
   }
}
Hva foregår her? Vi ser to svært like situasjoner. I tilfellet kaster vi et Stringobjekt til et Objectobjekt. Det er ingen problemer her - alt fungerer som forventet. Men i den andre situasjonen genererer kompilatoren en feil. Men vi gjør det samme, gjør vi ikke? Denne gangen bruker vi bare en samling av flere objekter. Men hvorfor oppstår feilen? Hva er forskjellen? Støper vi en Stringgjenstand til en Objecteller 20 gjenstander? Det er et viktig skille mellom et objekt og en samling av objekter . Hvis Bklassen er et barn av Aklassen, Collection<B>er det ikke et barn av Collection<A>. Dette er grunnen til at vi ikke var i stand til å kaste vår List<String>til enList<Object>. Stringer et barn av Object, men List<String>er ikke et barn av List<Object>. Dette virker kanskje ikke superintuitivt. Hvorfor gjorde språkets skapere det på denne måten? La oss forestille oss at kompilatoren ikke gir oss en feil:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
I dette tilfellet kan vi for eksempel gjøre følgende:
objects.add(new Object());
String s = strings.get(0);
Fordi kompilatoren ikke ga oss noen feil og tillot oss å lage en List<Object>referanse som peker til strings, kan vi legge til et hvilket som helst gammelt Objectobjekt i stringssamlingen! Dermed har vi mistet garantien for at samlingen vår bare inneholder objektene Stringspesifisert av type-argumentet i den generiske type-påkallelsen . Med andre ord har vi mistet hovedfordelen med generiske legemidler – typesikkerhet. Og fordi kompilatoren ikke stoppet oss fra å gjøre dette, vil vi bare få en feil under kjøring, som alltid er mye verre enn en kompileringsfeil. For å forhindre situasjoner som dette gir kompilatoren oss en feilmelding:
// Compilation error
List<Object> objects = strings;
...og minner oss om at List<String>det ikke er en etterkommer av List<Object>. Dette er en jernbelagt regel for generika, og den må huskes når du arbeider med dem. La oss gå videre. Anta at vi har et lite klassehierarki:
public class Animal {

   public void feed() {

       System.out.println("Animal.feed()");
   }
}

public class Pet extends Animal {

   public void call() {

       System.out.println("Pet.call()");
   }
}

public class Cat extends Pet {

   public void meow() {

       System.out.println("Cat.meow()");
   }
}
Hierarkiet toppes av en enkel dyreklasse, som er arvet av Pet. Kjæledyr har 2 underklasser: Hund og Katt. Anta nå at vi må lage en enkel iterateAnimals()metode. Metoden bør ta en samling av alle dyr ( Animal, Pet, Cat, Dog), iterere over alle elementene og vise en melding på konsollen under hver iterasjon. La oss prøve å skrive en slik metode:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Det ser ut til at problemet er løst! Imidlertid, som vi nylig har lært, List<Cat>og List<Dog>er List<Pet>ikke etterkommere av List<Animal>! Dette betyr at når vi prøver å kalle iterateAnimals()metoden med en liste over katter, får vi en kompileringsfeil:
import java.util.*;

public class Main3 {


   public static void iterateAnimals(Collection<Animal> animals) {

       for(Animal animal: animals) {

           System.out.println("Another iteration in the loop!");
       }
   }

   public static void main(String[] args) {


       List<Cat> cats = new ArrayList<>();
       cats.add(new Cat());
       cats.add(new Cat());
       cats.add(new Cat());
       cats.add(new Cat());

       // Compilation error!
       iterateAnimals(cats);
   }
}
Situasjonen ser ikke bra ut for oss! Må vi skrive separate metoder for å telle opp hver type dyr? Faktisk, nei, det gjør vi ikke :) Og som det skjer, jokertegn hjelper oss med dette! Vi kan løse problemet med en enkel metode ved å bruke følgende konstruksjon:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Dette er et jokertegn. Mer presist er dette den første av flere typer jokertegn. Det er kjent som jokertegn med øvre grense og uttrykkes av ? strekker seg . Hva forteller denne konstruksjonen oss? Dette betyr at metoden aksepterer en samling Animalobjekter eller en samling objekter av en hvilken som helst klasse som stammer fra Animal(? strekker seg til Animal). Med andre ord kan metoden akseptere en samling av Animal, Pet, Dog, eller Catobjekter - det gjør ingen forskjell. La oss overbevise oss selv om at det fungerer:
public static void main(String[] args) {

   List<Animal> animals = new ArrayList<>();
   animals.add(new Animal());
   animals.add(new Animal());

   List<Pet> pets = new ArrayList<>();
   pets.add(new Pet());
   pets.add(new Pet());

   List<Cat> cats = new ArrayList<>();
   cats.add(new Cat());
   cats.add(new Cat());

   List<Dog> dogs = new ArrayList<>();
   dogs.add(new Dog());
   dogs.add(new Dog());

   iterateAnimals(animals);
   iterateAnimals(pets);
   iterateAnimals(cats);
   iterateAnimals(dogs);
}
Konsoll utgang:
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Vi opprettet totalt 4 samlinger og 8 objekter, og det er nøyaktig 8 oppføringer på konsollen. Alt fungerer utmerket! :) Jokertegnet tillot oss å enkelt tilpasse den nødvendige logikken knyttet til spesifikke typer i en enkelt metode. Vi eliminerte behovet for å skrive en egen metode for hver type dyr. Tenk deg hvor mange metoder vi hadde trengt hvis søknaden vår ble brukt av en dyrehage eller et veterinærkontor :) Men la oss nå se på en annen situasjon. Arvehierarkiet vårt forblir uendret: klassen på øverste nivå er Animal, med Petklassen rett under, og klassene Catog Dogpå neste nivå. Nå må du omskrive iterateAnimals()metoden slik at den fungerer med alle typer dyr, bortsett fra hunder . Det vil si at den skal akseptere Collection<Animal>,Collection<Pet>eller Collection<Car>, men det skal ikke fungere med Collection<Dog>. Hvordan kan vi oppnå dette? Det ser ut til at vi igjen står overfor utsiktene til å skrive en egen metode for hver type :/ Hvordan skal vi ellers forklare kompilatoren hva vi ønsker skal skje? Det er faktisk ganske enkelt! Nok en gang kommer jokertegn oss til unnsetning her. Men denne gangen vil vi bruke en annen type jokertegn - et jokertegn med lavere grenser , som uttrykkes ved hjelp av super .
public static void iterateAnimals(Collection<? super Cat> animals) {

   for(int i = 0; i < animals.size(); i++) {

       System.out.println("Another iteration in the loop!");
   }
}
Her er prinsippet likt. Konstruksjonen <? super Cat>forteller kompilatoren at iterateAnimals()metoden kan akseptere en samling av Catobjekter eller en hvilken som helst stamfar til Catklassen som input som input. I dette tilfellet samsvarer Catklassen, dens overordnede Pet, og forelderen til dens overordnede, Animal, alle denne beskrivelsen. Klassen Dogsamsvarer ikke med begrensningene våre, så et forsøk på å bruke metoden med et List<Dog>argument vil resultere i en kompileringsfeil:
public static void main(String[] args) {

   List<Animal> animals = new ArrayList<>();
   animals.add(new Animal());
   animals.add(new Animal());

   List<Pet> pets = new ArrayList<>();
   pets.add(new Pet());
   pets.add(new Pet());

   List<Cat> cats = new ArrayList<>();
   cats.add(new Cat());
   cats.add(new Cat());

   List<Dog> dogs = new ArrayList<>();
   dogs.add(new Dog());
   dogs.add(new Dog());

   iterateAnimals(animals);
   iterateAnimals(pets);
   iterateAnimals(cats);

   // Compilation error!
   iterateAnimals(dogs);
}
Vi har løst problemet vårt, og nok en gang viste jokertegn seg å være ekstremt nyttige :) Med dette har leksjonen kommet til en slutt. Nå ser du hvor viktig generika er i studiet av Java – vi har hatt 4 hele leksjoner om dem! Men nå er du godt kjent med temaet og du kan bevise ferdighetene dine i jobbintervjuer :) Og nå er det på tide å komme tilbake til oppgavene! Lykke til med studiene! :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå