Kategória: Standard Java.
A Java-ban a generikus típusok az 5-ös verzióban jelentek meg. Ez lehetővé teszi azt, hogy olyan osztályokat és függvényeket készítsünk, amelyeknél nem adjuk meg a típust, így többféle típussal létrehozhatjuk ill. lefuttathatjuk. A generikus típust csúcsos zárójelek (< és >) közé kell tennünk, pl. így: <T>. Lássunk egy példát!
// GenericsExample.java public class GenericsExample<T> { T object; public GenericsExample(T object) { this.object = object; } public void fancyPrint() { System.out.println("> " + object + " <"); } } // Main.java public class Main { public static <E extends Comparable<E>> E getMaxElement(E[] inputArray) { if (inputArray.length == 0) { return null; } E max = inputArray[0]; for (E element : inputArray) { if (element.compareTo(max) > 0) { max = element; } } return max; } public static void main(String[] args) { GenericsExample<Integer> geInt = new GenericsExample<Integer>(15); geInt.fancyPrint(); GenericsExample<String> geString = new GenericsExample<String>("apple"); geString.fancyPrint(); Integer[] intArray = {4, 8, 2}; Double[] doubleArray = {4.89, -5.2, 1.0}; Character[] charArray = {'a', 'p', 'p', 'l', 'e'}; System.out.println(getMaxElement(intArray)); System.out.println(getMaxElement(doubleArray)); System.out.println(getMaxElement(charArray)); } }
A példában a Comparable egy olyan interfész, amely maga is generikus, és egy metódust definiál: int compareTo(o); ha az adott objektum a nagyobb, akkor a visszatérési érték pozitív, ha a másik (azaz a paraméterül átadott o), akkor negatív, és ha egyenlőek, akkor 0.
Az egész értelme leginkább a Collections esetén van, amit lejjebb látunk majd részletesebben.
A GenericsExample osztály egy osztály szintű generikus típust tartalmaz, amit T-vel jelölünk. Az osztályban létrehozott fancyPrint() metódus csúcsos zárójelek közé helyezve kiírja a generikus típusú attribútumot. Ezt a főprogramból kétféleképpen hívjuk meg: először egész számmal (Integer), másodszor szöveges típussal (String).
A második példa a főosztályba került. Ez egy generikus függvény, a generikus típus tehát itt függvény szinten van megadva, nem osztály szinten. A típusnév (E) után találunk egy extends kulcsszót; ezzel azt jelöljük, hogy a konkrét típus nem lehet akármilyen, hanem csak olyan, amely megvalósítja a Comparable interfészt. A függvény maga, kihasználva azt, hogy meghívhatja a compareTo() függvényt, meghatározza a maximális elemet.
Összefoglalva és kiegészítve:
- A generikus típust csúcsos zárójelbe tesszük, így: <T>
- A típust általában egy nagybetűs karakterrel adjuk meg. Néhány konvenció:
- T: általános típus. Ha szükség van továbbiakra: S, U, V stb.
- K és V: kulcs (key) és érték (value).
- E: elem (element) különböző adatszerkezetek esetén.
- N: szám (number).
- Léteznek generikus osztályok és generikus függvények; a generikus típus jelölést mindkét esetben a név elé kell tenni.
- Az extends kulcsszóval adhatjuk meg az, hogy a generikus típus csak egy adott interfész megvalósítása vagy adott osztály leszármazottja lehet, pl. <N extends Number>.
- A generikus típusra az öröklődés nem vonatkozik, pl. az Integer a Number osztályból öröklődik, de a GenericsExample<Number> nem őse a GenericsExample<Integer>-nek, azaz az előbbinek nem adhatjuk utóbbit értékül. (Emlékeztetőül: egy Number típusnak értékül adhatunk Integer-t).
- Egyszerre megadhatunk több megszorítást is & jellel elválasztva (melyek közül értelemszerűen legfeljebb egy lehet osztály), pl. <T extends Comparable & Serializable>.
- Paraméterek, mezők, lokális változók és visszatérési értékek esetén használhatjuk a dzsóker paramétert, ami a kérdőjel (<?>). Ennek adhatunk alsó korlátot (pl. ? extends Number) vagy felső korlátot (pl. ? super Integer).