En generisk type er en generisk klasse eller grænseflade, der er parametreret over typer. Følgende Box-klasse ændres for at demonstrere konceptet.
En simpel Box-klasse
Begynd med at undersøge en ikke-generisk Box-klasse, der fungerer på objekter af enhver type. Det behøver kun at give to metoder: set, som tilføjer et objekt til feltet, og get, som henter det:
Da dets metoder accepterer eller returnerer et objekt, er du fri til at videregive alt hvad du vil , forudsat at det ikke er en af de primitive typer. Der er ingen måde at verificere, på tidspunktet for kompilering, hvordan klassen bruges. En del af koden placerer muligvis et heltal i feltet og forventer at få heltal ud af det, mens en anden del af koden fejlagtigt kan passere i en streng, hvilket resulterer i en runtime-fejl.
En generisk version af Box Class
En generisk klasse defineres med følgende format:
class name<T1, T2, ..., Tn> { /* ... */ }
For at opdatere Box klasse for at bruge generics, du opretter en generisk typedeklaration ved at ændre koden “public class Box” til “public class Box < T > “. Dette introducerer typevariablen, T, der kan bruges hvor som helst inden for klassen.
Med denne ændring bliver Box-klassen:
Som du kan se, er alle forekomster af objekt erstattet af T. En typevariabel kan være en hvilken som helst ikke-primitiv type, du angiver: enhver klassetype, enhver interface-type, enhver array-type eller endda en anden type variabel.
Den samme teknik kan anvendes til at oprette generiske grænseflader.
Type Parameter Navngivningskonventioner
Efter konvention er typeparameternavne enkelt, store bogstaver. Dette står i skarp kontrast til de konventionelle navngivningskonventioner, som du allerede kender til, og med god grund: Uden denne konvention ville det være svært at se forskellen mellem en typevariabel og et almindeligt klasse- eller interface-navn.
De mest anvendte typeparameternavne er:
- E – Element (bruges i vid udstrækning af Java Collections Framework)
- K – Nøgle
- N – nummer
- T – type
- V – værdi
- S, U, V osv. – 2., 3., 4. type
Du kan se disse navne, der bruges i Java SE API og resten af denne lektion.
Påkald og instantiering af en generisk type
For at henvise til den generiske Box-klasse indefra din kode skal du udføre en generisk type påkaldelse, der erstatter T med en vis konkret værdi, f.eks. Heltal:
Box<Integer> integerBox;
Du kan tænke på en generisk indkaldelse som ligner en almindelig metodeopkald, men i stedet for at give et argument til am ethod, du sender et typeargument – Heltal i dette tilfælde – til selve Box-klassen.
Som enhver anden variabelerklæring opretter denne kode faktisk ikke et nyt Box-objekt. Det erklærer simpelthen, at integerBox vil indeholde en henvisning til en “Box of Integer”, hvilket er, hvordan Box < Heltal > læses.
En påkaldelse af en generisk type kaldes generelt en parametreret type.
For at instantiere denne klasse skal du bruge det nye nøgleord som normalt, men placere < Heltal > mellem klassens navn og parentesen:
Box<Integer> integerBox = new Box<Integer>();
Diamanten
I Java SE 7 og nyere kan du erstatte de typeargumenter, der kræves for at påkalde konstruktøren af en generisk klasse med et tomt sæt af typeargumenter (< >) så længe compileren kan bestemme eller udlede typeargumenterne fra konteksten. Dette par vinkelbeslag, < >, kaldes uformelt diamanten. For eksempel kan du oprette en forekomst af Box < Heltal > med følgende udsagn:
Box<Integer> integerBox = new Box<>();
For mere information om diamantnotation og typeinferens, se Type Inference.
Flere typeparametre
Som tidligere nævnt, en generisk klasse kan have flere parametre.For eksempel den generiske OrderedPair-klasse, der implementerer den generiske Pair-grænseflade:
Følgende udsagn skaber to instantieringer af OrderedPair-klassen:
Koden, ny OrderedPair < Streng, heltal >, instanterer K som en streng og V som et heltal. Derfor er parametertyperne for OrderedPair’s konstruktør henholdsvis String og Integer. På grund af autoboxing er det gyldigt at sende en streng og et int til klassen.
Som nævnt i The Diamond, fordi en Java-kompilator kan udlede K- og V-typerne fra erklæringen OrderedPair < Streng, heltal >, disse udsagn kan afkortes ved hjælp af diamantnotation:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
For at oprette en generisk grænseflade skal du følge de samme konventioner som for oprettelse af en generisk klasse.
Parameteriserede typer
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));