CodeGym/Java blog/Tilfældig/Jokertegn i generiske lægemidler
John Squirrels
Niveau
San Francisco

Jokertegn i generiske lægemidler

Udgivet i gruppen
Hej! Lad os fortsætte vores undersøgelse af generiske lægemidler. Du har allerede fået en betydelig mængde viden om dem fra tidligere lektioner (om at bruge varargs, når du arbejder med generiske stoffer og om typesletning ), men der er et vigtigt emne, som vi endnu ikke har overvejet - jokertegn . Dette er meget vigtigt træk ved generiske lægemidler. Så meget, at vi har dedikeret en separat lektion til det! Når det er sagt, er der ikke noget særligt kompliceret ved jokertegn. Det vil du se med det samme :) Jokertegn i generiske tegn - 1Lad os 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;
   }
}
Hvad sker der her? Vi ser to meget lignende situationer. I tilfældet kaster vi en Stringgenstand til en Objectgenstand. Der er ingen problemer her - alt fungerer som forventet. Men i den anden situation genererer compileren en fejl. Men vi gør det samme, gør vi ikke? Denne gang bruger vi blot en samling af flere genstande. Men hvorfor opstår fejlen? Hvad er forskellen? Støber vi en Stringgenstand til en Objecteller 20 objekter? Der er en vigtig skelnen mellem et objekt og en samling af objekter . Hvis Bklassen er et barn af Aklassen, så Collection<B>er det ikke et barn af Collection<A>. Det er derfor, vi ikke var i stand til at kaste vores List<String>til enList<Object>. Stringer et barn af Object, men List<String>er ikke et barn af List<Object>. Dette virker måske ikke super intuitivt. Hvorfor gjorde sprogets skabere det på denne måde? Lad os forestille os, at compileren ikke giver os en fejl:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
I dette tilfælde kunne vi for eksempel gøre følgende:
objects.add(new Object());
String s = strings.get(0);
Fordi compileren ikke gav os nogen fejl og tillod os at oprette en List<Object>reference, der peger på strings, kan vi tilføje et hvilket som helst gammelt Objectobjekt til stringssamlingen! Dermed har vi mistet garantien for, at vores samling kun indeholder de Stringobjekter, der er specificeret af type-argumentet i den generiske type-invokation . Med andre ord har vi mistet den største fordel ved generiske lægemidler - typesikkerhed. Og fordi compileren ikke forhindrede os i at gøre dette, vil vi kun få en fejl under kørsel, hvilket altid er meget værre end en kompileringsfejl. For at forhindre situationer som denne giver compileren os en fejl:
// Compilation error
List<Object> objects = strings;
...og minder os om, at det List<String>ikke er en efterkommer af List<Object>. Dette er en jernbeklædt regel for generika, og den skal huskes, når du arbejder med dem. Lad os gå videre. Antag, at vi har et lille 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 af en simpel dyreklasse, som er arvet af Pet. Pet har 2 underklasser: Hund og Kat. Antag nu, at vi skal lave en simpel iterateAnimals()metode. Metoden bør tage en samling af alle dyr ( Animal, Pet, Cat, Dog), iterere over alle elementerne og vise en besked på konsollen under hver iteration. Lad os prøve at skrive en sådan metode:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Det ser ud til, at problemet er løst! Men som vi for nylig lærte, List<Cat>og List<Dog>er List<Pet>ikke efterkommere af List<Animal>! Det betyder, at når vi forsøger at kalde iterateAnimals()metoden med en liste over katte, får vi en kompileringsfejl:
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);
   }
}
Situationen ser ikke særlig god ud for os! Skal vi skrive separate metoder til at opregne hver slags dyr? Faktisk, nej, det gør vi ikke :) Og som det sker, hjælper jokertegn os med dette! Vi kan løse problemet med en simpel metode ved at bruge følgende konstruktion:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Dette er et jokertegn. Mere præcist er dette den første af flere typer jokertegn. Det er kendt som et jokertegn med øvre grænse og er udtrykt ved ? strækker sig . Hvad fortæller denne konstruktion os? Det betyder, at metoden accepterer en samling af Animalobjekter eller en samling af objekter af enhver klasse, der stammer fra Animal(? strækker sig Animal). Med andre ord kan metoden acceptere en samling af Animal, Pet, Dog, eller Catobjekter - det gør ingen forskel. Lad os overbevise os selv om, at det virker:
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);
}
Konsoludgang:
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 oprettede i alt 4 samlinger og 8 objekter, og der er præcis 8 poster på konsollen. Alt fungerer fantastisk! :) Jokertegnet tillod os nemt at tilpasse den nødvendige logik knyttet til specifikke typer i en enkelt metode. Vi eliminerede behovet for at skrive en separat metode for hver type dyr. Forestil dig, hvor mange metoder vi ville have haft brug for, hvis vores ansøgning blev brugt af en zoologisk have eller et veterinærkontor :) Men lad os nu se på en anden situation. Vores arvehierarki forbliver uændret: klassen på øverste niveau er Animal, med Petklassen lige under, og klasserne Catog Dogpå det næste niveau. Nu skal du omskrive iterateAnimals()metoden, så den fungerer med alle typer dyr, undtagen hunde . Det vil sige, den skal acceptere Collection<Animal>,Collection<Pet>eller Collection<Car>, men det burde ikke fungere med Collection<Dog>. Hvordan kan vi opnå dette? Det ser ud til, at vi igen står over for udsigten til at skrive en separat metode for hver type :/ Hvordan skal vi ellers forklare kompilatoren, hvad vi ønsker, der skal ske? Det er faktisk ret simpelt! Endnu en gang kommer jokertegn os til hjælp her. Men denne gang vil vi bruge en anden type jokertegn — et jokertegn med lavere grænser , som udtrykkes ved hjælp af 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 princippet det samme. Konstruktionen <? super Cat>fortæller compileren, at iterateAnimals()metoden som input kan acceptere en samling af Catobjekter eller enhver forfader til Catklassen som input. I dette tilfælde matcher Catklassen, dens overordnede Pet, og forælderen til dens overordnede, Animal, alle denne beskrivelse. Klassen Dogmatcher ikke vores begrænsning, så et forsøg på at bruge metoden med et List<Dog>argument vil resultere i en kompileringsfejl:
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 vores problem, og igen viste jokertegn sig at være yderst nyttige :) Hermed er lektionen slut. Nu kan du se, hvor vigtige generika er i dit studie af Java - vi har haft 4 hele lektioner om dem! Men nu er du velbevandret i emnet, og du kan bevise dine evner i jobsamtaler :) Og nu er det tid til at komme tilbage til opgaverne! Held og lykke med dine studier! :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du skal være logget ind for at skrive en kommentar
Denne side har ingen kommentarer endnu