Introductie
Generics, oftwel Generieken, die in Java 5.0 is geintroduceerd slaat op de harde typering van de gebruikte objecten in collecties, mappen, klassen en methoden. Het gebruik hiervan is niet verplicht, maar het kan uiteindelijk erg handig zijn. Stel je een ArrayList voor dat (volgens jouw code!) alleen maar Integer objecten kan bevatten:
package test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// Declareer en initialiseer een algemene ArrayList.
List arrayList = new ArrayList();
// Voeg de Integer waarden toe.
arrayList.add(new Integer("1"));
arrayList.add(new Integer("2"));
arrayList.add(new Integer("3"));
// Doorloop de waarden.
for (Iterator iter = arrayList.iterator(); iter.hasNext();) {
// iter.next() retourneert een Object, je moet deze casten naar Integer.
Integer integer = (Integer) iter.next();
// Laat de waarde zien.
System.out.println(integer);
}
}
}
1
2
3
Stel dat je al dan niet onbewust een String object aan de arrayList toevoegt, dan zou die cast naar Integer problemen opleveren: er wordt een ClassCastException afgeworpen. Een String kan namelijk niet gecast worden naar een Integer.
package test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// Declareer en initialiseer een algemene ArrayList.
List arrayList = new ArrayList();
// Voeg de Integer waarden toe.
arrayList.add(new Integer("1"));
arrayList.add(new Integer("2"));
arrayList.add("3"); // Let op: String ipv Integer.
// Doorloop de waarden.
for (Iterator iter = arrayList.iterator(); iter.hasNext();) {
// iter.next() retourneert een Object, je moet deze casten naar Integer.
Integer integer = (Integer) iter.next();
// Laat de waarde zien.
System.out.println(integer);
}
}
}
1
2
Exception in thread "main" java.lang.ClassCastException: java.lang.String
at test.Test.main(Test.java:23)
De foutmelding spreekt voor zich: je kunt een object van het type java.lang.String niet casten naar een Integer.
Met Generics kun je dit soort problemen dus vroeg afvangen door alvast in de code de ArrayList te aanmerken als een lijst dat alleen maar objecten van het Integer type mag bevatten. De Java compiler zal dat detecteren en zal indien nodig alvast een compilatie fout melden wanneer je een object van het verkeerde type aan de lijst toevoegt. Dit gebeurt in principe dus vóórdat je de code kunt uitvoeren.
Terug naar boven
Generieke collecties
Wanneer je Generics wilt toepassen in collecties, dan moet je ze tussen de vishaken toevoegen aan de declaratie en instantiatie van het object. Hier heb je enkele voorbeelden:
List<Type> arrayList = new ArrayList<Type>();
Set<Type> treeSet = new TreeSet<Type>();
Het Type moet minimaal overeenkomen met het object type van de inhoud van de collectie. Minimaal, dus je kunt ook de supertype opgeven. In het geval van een Integer kun je dus gewoon Integer opgeven, of diens superklasse Number of zelfs heel drastisch de hoofdklasse Object. Wanneer je Integer opgeeft, dan kun je alleen objecten van dezelfde type of objecten van diens (op het moment niet-bestaande) subklasse in de collectie zetten. In het geval van Number kun je dus alleen objecten van de types Number, Integer, Long, Short, etc in de collectie stoppen. De officiële subklassen van Number kun je in de API documentatie vinden onder "Direct Known Subclasses". Wanneer je de type Object opgeeft, dan kun je wel raden dat je élk mogelijk type in de collectie kunt stoppen, wat de collectie eigenlijk ongeneriek zal maken ;)
We passen de oorsronkelijke code even aan met de Generics:
package test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// Declareer en initialiseer een ArrayList voor Integers.
Listintegers = new ArrayList ();
// Voeg de Integer waarden toe.
integers.add(new Integer("1"));
integers.add(new Integer("2"));
integers.add(new Integer("3"));
// Doorloop de waarden.
for (Iteratoriter = integers.iterator(); iter.hasNext();) {
// Dankzij de Generics is een cast niet nodig. De compiler weet in dit
// geval immers al van te voren uit welke object type de collectie bestaat.
Integer integer = iter.next();
// Laat de waarde zien.
System.out.println(integer);
}
}
}
1
2
3
En nu proberen we een ordinaire String object aan de lijst te toevoegen:
...
// Voeg de Integer waarden toe.
integers.add(new Integer("1"));
integers.add(new Integer("2"));
integers.add("3"); // Let op: String ipv Integer.
...
The method add(Integer) in the type List
Deze code zal dus niet compileren en de bovenstaande compilatiefout teruggeven en deze code zal derhalve niet zomaar uitgevoerd kunnen worden. In een goede IDE, zoals Eclipse, krijg je ook een foutmelding te zien bij de add() methode.
Wanneer je de object type Number opgeeft, die zelf ook een tal subklassen heeft, dan kun je zonder problemen verschillende object typen van de superklasse Number in de collectie stoppen.
package test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// Declareer en initialiseer een ArrayList voor Numbers.
Listnummers = new ArrayList ();
// Voeg de Number waarden toe.
nummers.add(new Integer("1"));
nummers.add(new Long("2"));
nummers.add(new Short("3"));
// Doorloop de waarden.
for (Iteratoriter = nummers.iterator(); iter.hasNext();) {
// Dankzij de Generics is een cast niet nodig. De compiler weet in dit
// geval immers al van te voren uit welke object type de collectie bestaat.
Number nummer = iter.next();
// Laat de waarde zien.
System.out.println(nummer);
}
}
}
1
2
3
Sinds Java 5.0 is er naast de bestaande Iterator een nieuwe manier om de collectie te doorlopen, namelijk die met de for statement voor array's. We passen de bovenstaande code even hierop aan:
...
// Doorloop de nummers.
for (Number nummer : nummers) {
// Laat de nummer zien.
System.out.println(nummer);
}
...
De in de for statement gedeclareerde type moet minimaal overeenkomen met het object type van de inhoud van de collectie. Je zou boven Number dus ook Object kunnen opgeven. Het is maar net wat je met de doorgelopen waarden wilt doen.
Terug naar boven
Generieke mappen
Je kunt de Generics ook toepassen in mappen, hierbij volg je dezelfde richtlijnen als bij generieke collecties, met het verschil dat je zowel de sleutel als de waarde moet definiëren bij de declaratie en instantiatie van het object. Hier heb je enkele voorbeelden:
Map<SleutelType, WaardeType> hashMap = new HashMap<SleutelType, WaardeType>();
Map<SleutelType, WaardeType> hashtable = new Hashtable<SleutelType, WaardeType>();
Het SleutelType moet dus minimaal overeenkomen met het object type van de sleutels van de map en het WaardeType moet dus minimaal overeenkomen met het object type van de waarden van de map. Voor de rest gelden dezelfde richtlijnen als bij de collecties.
Even wat voorbeeldcode:
package test;
import java.util.LinkedHashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
// Declareer en initialiseer een LinkedHashMap voor Integers
// met een String sleutel.
Mapmap = new LinkedHashMap ();
// Voeg de Integer waarden met String sleutels toe.
map.put("Een", new Integer("1"));
map.put("Twee", new Integer("2"));
map.put("Drie", new Integer("3"));
// Doorloop de sleutels.
for (String sleutel : map.keySet()) {
// Laat de sleutel en de waarde zien.
System.out.println(sleutel + ", " + map.get(sleutel));
}
}
}
Een, 1
Twee, 2
Drie, 3
Terug naar boven
Generieke klassen
Je kunt de Generics ook toepassen in klassen. Dit gaat een stuk verder dan het toepassen van de Generics in collecties en mappen. Er wordt namelijk niet gekeken naar de directe subklassen of superklassen van het opgegeven type. Dit moet je zelf aangeven met behulp van de extends respectievelijk super sleutelwoorden.
Class<Type> c = Klasse van de gegeven type
Classextends Type> c = (Sub)klasse van de gegeven type
Classsuper Type> c = (Super)klasse van de gegeven type
Class c = Willekeurige klasse
Hieronder volgen enkele voorbeelden, compleet met gesimuleerde fouten (rood onderstreept):
// Declareer en instantieer een klasse waarvan de type
// strikt gelijk aan de gegeven type is.
Classc1 = Number.class; // Number is niet gelijk aan Integer.
Classc2 = Number.class;
Class