En generisk typ är en generisk klass eller ett gränssnitt som parametreras över olika typer. Följande Box-klass kommer att modifieras för att demonstrera konceptet.
En enkel Box-klass
Börja med att undersöka en icke-generisk Box-klass som fungerar på objekt av vilken typ som helst. Det behöver bara tillhandahålla två metoder: set, som lägger till ett objekt i rutan, och get, som hämtar det:
Eftersom dess metoder accepterar eller returnerar ett objekt, är du fri att passera vad du vill , förutsatt att det inte är en av de primitiva typerna. Det finns inget sätt att verifiera hur klassen används vid sammanställningstidpunkten. En del av koden kan placera ett heltal i rutan och förvänta sig att få heltal ur den, medan en annan del av koden felaktigt kan passera i en sträng, vilket resulterar i ett körtidsfel.
En generisk version i rutan Klass
En generisk klass definieras med följande format:
class name<T1, T2, ..., Tn> { /* ... */ }
För att uppdatera Boxklass för att använda generics, du skapar en generisk typdeklaration genom att ändra koden ”public class Box” till ”public class Box < T > ”. Detta introducerar typvariabeln, T, som kan användas var som helst inom klassen.
Med denna ändring blir Box-klassen:
Som du kan se är alla förekomster av objekt ersatt av T. En typvariabel kan vara vilken som helst icke-primitiv typ du anger: vilken klasstyp som helst, vilken gränssnitttyp som helst, vilken matris som helst eller till och med en annan typvariabel.
Samma teknik kan användas för att skapa generiska gränssnitt.
Typparameter Namngivningskonventioner
Enligt konvention är typparameternamn enstaka versaler. Detta står i skarp kontrast till de variabla namngivningskonventionerna som du redan känner till och med goda skäl: Utan denna konvention skulle det vara svårt att se skillnaden mellan en typvariabel och ett vanligt klass- eller gränssnittsnamn.
De vanligaste typparameternamnen är:
- E – Element (används i stor utsträckning av Java Collections Framework)
- K – Nyckel
- N – nummer
- T – typ
- V – värde
- S, U, V etc. – andra, tredje, fjärde typen
Du kommer att se dessa namn som används i Java SE API och resten av den här lektionen.
Anropa och installera en generisk typ
För att referera till den generiska Box-klassen inifrån din kod måste du utföra en generisk typ av anrop som ersätter T med något konkret värde, till exempel heltal:
Box<Integer> integerBox;
Du kan tänka dig att en generisk typ av anrop liknar en vanlig metodanrop, men istället för att skicka ett argument till am ethod, du skickar ett typargument – heltal i det här fallet – till själva Box-klassen.
Som alla andra variabeldeklarationer skapar den här koden inte ett nytt Box-objekt. Det förklarar helt enkelt att integerBox kommer att innehålla en hänvisning till en ”Box of Integer”, vilket är hur Box < Heltal > läses.
En anrop av en generisk typ är allmänt känd som en parametrerad typ.
För att starta denna klass använder du det nya nyckelordet som vanligt, men placerar < Heltal > mellan klassnamnet och parentesen:
Box<Integer> integerBox = new Box<Integer>();
Diamanten
I Java SE 7 och senare kan du ersätta de typargument som krävs för att åberopa konstruktören för en generisk klass med en tom uppsättning typargument (< >) så länge kompilatorn kan bestämma eller dra slutsatsen om typargumenten från sammanhanget. Detta par vinkelfästen, < >, kallas informellt diamanten. Du kan till exempel skapa en instans av Box < Heltal > med följande uttalande:
Box<Integer> integerBox = new Box<>();
För mer information om diamantnotering och typinferens, seTypinferens.
Flera typparametrar
Som nämnts tidigare en generisk klass kan ha flera typparametrar.Till exempel den generiska OrderedPair-klassen, som implementerar det generiska Pair-gränssnittet:
Följande påståenden skapar två instantiations av OrderedPair-klassen:
Koden, ny OrderedPair < Sträng, heltal >, initierar K som en sträng och V som ett heltal. Därför är parametertyperna för OrderedPair-konstruktören String respektive Integer. På grund av autoboxing är det giltigt att skicka en String och ett int till klassen.
Som nämnts i The Diamond, eftersom en Java-kompilatorn kan härleda K- och V-typerna från deklarationen OrderedPair < Sträng, heltal >, dessa uttalanden kan förkortas med diamantnotation:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
För att skapa ett generiskt gränssnitt, följ samma konventioner som för att skapa en generisk klass.
Parameteriserade typer
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));