Javakurs für Anfänger
Einheit 14: Generics
Lorenz Schauer
Lehrstuhl für Mobile und Verteilte Systeme
Heutige Agenda
Generische Klassen (Generics)
 Motivation
 Java Typ-Prüfung
 Warum also Generics?
 Generische Typen selbst definieren
 Generische Typen anwenden
Zusammenfassung
Ausblick
Praxis:
 Generische Klasse BlackBox
schreiben
Lernziele
 Einführung in generische Klassen
 Das Konzept von Generics kennenlernen und verstehen
 Generische Typen schreiben und v.a. benutzen können
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
2
Motivation
Generische Typen (Generics):
 Sind ein syntaktisches Mittel für die generische Programmierung
 Zählen zu den komplexesten Typen in Java
 Seit Java 1.5 fester Bestandteil
 Grundidee: Einführung von Variablen, die Typen (statt wie bisher Werte)
speichern
 Diese Typ-Variablen repräsentieren zum Zeitpunkt der Implementierung unbekannte
Typen.
 Erst bei der Verwendung der Klassen, Schnittstellen und Methoden werden diese
Typ-Variablen durch konkrete Typen ersetzt.
 Damit kann typsichere Programmierung gewährleistet werden.
 Ein bekanntes Beispiel (hatten wir schon):
 Java.util.ArrayList<E>
 Anwendung:
ArrayList<String> meine_liste1 = new ArrayList<String>();
ArrayList<Integer> meine_liste2 = new ArrayList<Integer>();
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
3
Java Typ-Prüfung
Die Typ-Prüfung passiert 2 Mal:
 Durch den Compiler (statisch)
 Führt erste Typ-Prüfung durch.
 Ist weniger „exakt“ als die dynamische Prüfung
 Durch die Laufzeitumgebung JVM (dynamisch)
 Hat absolute Typ-Intelligenz
 Prüft zur Laufzeit (als letzte Instanz), ob der Typ zum zugewiesenen Objekt passt
 Falls nicht, folgt Exception
// Beispiel:
Object o = Integer.valueOf(10);
String s = (String) o;
System.out.println(s);
Anweisungen sind für den Compiler
zunächst völlig in Ordnung.
Statische Typprüfung: korrekt
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer
cannot be cast to java.lang.String
Aber die JVM kann diese Anweisungen nicht
at Test.main(Test.java:8)
ausführen => ClassCastException
Dynamische Typprüfung: falsch!
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
4
Warum also Generics?
Einführung von Generics:
 Compiler bekommt mehr Informationen über die Typen
 So sollen bspw. ClassCastExceptions vermeiden werden.
 Beispiel bei ArrayList:
Ohne parametrisierte Typen
Mit parametrisierten Typen
public static void main(String[] args) {
ArrayList myList = new ArrayList();
myList.add("Hallo");
myList.add(new Hund("Bello","Haski",98));
myList.add(new Hund("Flippo","Schaefer",101));
myList.add("Bello");
for(int i=0; i<myList.size();i++){
Hund h = (Hund) myList.get(i);
System.out.println(h.getName());
}
}
Exception in thread "main"
java.lang.ClassCastException:
java.lang.String cannot be cast to Hund
at Test.main(Test.java:15)
09.02.2017
Compiler-Fehler, da statische Typ-Prüfung
jetzt fehlschlägt!
Javakurs 14: Generics - Lorenz Schauer
5
Generische Typen deklarierten
Man kann eigene generische Typen (Klassen) definieren:
 public class MeineKlasse<T> {…}
 Objekterzeugung dann:
MeineKlasse<Typ> objektname = new MeineKlasse<Typ>();
 Bsp mit String:
MeineKlasse<String> k1 = new MeineKlasse<String>();
 Seit Java 7 auch möglich:
MeineKlasse<String> k1 = new MeineKlasse<>();
 Das T steht für den generischen Typen und kann nun als Typ-Variable in der Klasse
benutzt werden:
 Als Instanzvariable:
 Bsp.: private T meine_instanzvariable;
 Als Parameter:
 Bsp.: public MeineKlasse(T value){…}
 Als Rückgabetyp:
 Bsp.: public T doSomeStuff(){…}
Hinweis: Laut Konvention schreibt man T für Typen und E für Elemente
 Muss aber nicht sein, man kann auch <BlaDiBlub> verwenden
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
6
Allgemeines Beispiel
public class MeineKlasse<T> {
Generische Klasse
Mit Typ-Variable T
// Instanzvariablen
private T instanzvariable1;
private T instanzvariable2;
private String name;
private int alter;
Generische
(parametrisierte)
Instanzvariablen
// Konstruktoren
public MeineKlasse(){
this.name = "DefaultName";
this.alter = 0;
}
public MeineKlasse(T value1, T value2){
this.instanzvariable1=value1;
this.instanzvariable1=value2;
}
public MeineKlasse(T value1, T value2, String name, int alter){
this.instanzvariable1=value1;
this.instanzvariable1=value2;
this.name = name;
this.alter = alter;
}
//Getter and Setter
public T getInstanzvariable1() {
return instanzvariable1;
}
public void setInstanzvariable1(T instanzvariable1) {
this.instanzvariable1 = instanzvariable1;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
Nicht generische
Instanzvariablen
Konstruktor erwartet
die generischen Typen
Konstruktor erwartet
generische und nicht
generische Typen
Methode liefert
generischen Typ
zurück
Methode erwartet
generischen Typ
7
Wie wird die Klasse nun benutzt?
Ein Objekt der generischen Klasse kann nun beim Erzeugen angeben, welchen Typ
es in die Typ-Variable einsetzt.
 Analog zur ArrayList
 ArrayList<String> meineListe = new ArrayList<String>();
 ArrayList meineGenerelleListe = new ArrayList();
 Wird kein Typ angegeben wird implizit die Super-Klasse Object eingesetzt
 D.h.: Dann weiß der Compiler genauso wenig über die „echten“ Typen wie zuvor und
man muss auf Typ-Casting zurückgreifen!
 Das ist nicht der Sinn von Generics und wird mit einer Compiler-Warnung bestraft:
“ArrayList is a raw type. References to generic type ArrayList<E> should be
parameterized“
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
8
Wie wird die Klasse nun benutzt?
public class Test{
objekt1 legt keinen generischen
Typ fest.
=> Es wird implizit Object
verwendet
public static void main(String[] args) {
// Objekt erzeugen:
MeineKlasse objekt1 = new MeineKlasse();
MeineKlasse<String> objekt2=new MeineKlasse<>();
objekt2 setzt String
MeineKlasse<Integer> objekt3=new MeineKlasse<>(); objekt3 setzt Integer
objekt1 setzt Instanzvariable1
// Methoden-Aufrufe
auf „Peter“.
objekt1.setInstanzvariable1("Peter");
String s1 =(String)objekt1.getInstanzvariable1(); Korrekt, da generischer Typ =
Object
objekt2.setInstanzvariable1("Maria");
Bei Nutzung Typ-Cast nötig!
String s2 = objekt2.getInstanzvariable1();
objekt3.setInstanzvariable1(23);
int s3 = objekt3.getInstanzvariable1();
}
}
09.02.2017
objekt2 setzt Instanzvariable1 auf
„Maria“
Korrekt, da generischer Typ = String
objekt3 setzt Instanzvariable1 auf 23
Korrekt, da generischer Typ = int
Javakurs 14: Generics - Lorenz Schauer
9
Generische Typen einschränken
Manchmal ist der bloße Typ <T> zu generisch und man möchte die akzeptierten
Möglichkeiten einschränken:
 Mittels <T extends ClassOrInterface> lässt sich ein Typ bestimmen der
eine bestimmte Klasse erweitert oder ein Interface implementiert
 Bsp.:
 public class MeineKlasse <T extends CharSequence> {…}
 Die Erzeugung mittels new MeineKlasse<String>(); ist demnach erlaubt
 Eine Erzeugung mit new MeineKlasse<Integer>(); verbietet der Compiler
- Bound mismatch: The type Integer is not a valid substitute for
the bounded parameter <T extends CharSequence> of the type
MeineKLasse<T>
 Soll <T> zu mehreren Typen passen, kann man mittels & noch weitere Interfaces
hinzufügen
 Bsp.: public class MeineKlasse <T extends CharSequence & Comparable>
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
10
Programmieraufgabe zu Generics
Schreiben Sie in Analogie zur ArrayList eine generische Klasse BlackBox<E> welche folgende
Methoden anbieten soll:
 add(E element) fügt ein Element Ihrer Box hinzu
 remove(E element) löscht ein Element aus Ihrer Box
 clear() löscht alle Element aus Ihrer Box
 getElementAtPosition(int i) liefert das Element an der Position i zurück
 printNamesOfElements() gibt die Namen sämtlicher Elemente in der BlackBox auf der
Konsole aus
 Hinweis: Nutzen Sie hierfür die Methode toString(), welche von der Oberklasse Object
bereitgestellt wird.
Zusätzlich soll die Klasse folgende Attribute bereitstellen
 String name (welcher über den Konstruktor gesetzt wird)
 Eine geeignete Datenstruktur zum Speichern der Elemente
 Hinweis: Der Einfachheit halber nutzen Sie hier die ArrayList<E>, mit der Sie oben genannte
Methoden leicht implementieren können.
Nutzen Sie nun Ihre neue Klasse BlackBox und erzeugen Sie 2 Instanzen mit unterschiedlichen
Typen.
 Sie können auf Klassen von vorangegangenen Stunden zurückgreifen (Hund, Auto)
Erzeugen Sie nun mehrere Objekte der beiden gewählten Klassen und fügen Sie diese in die
entsprechene BlackBox ein.
Was erscheint auf der Konsole, wenn Sie die printNamesOfElements() Methode auf den
beiden BlackBox Objekten aufrufen?
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
11
Zusammenfassung
Sie haben nun die grundlegenden Elemente der objektorientierten
Programmiersprache Java kennengelernt:
 Klassen, Objekte, Variablen & Methoden
 Zusammenspiel verschiedener Klassen und Objekte
 Abstraktion und Datenkapselung
 Kontrollstrukturen
 Statische und dynamische Arrays
 Vererbung
 Subtyping, Typ-Casting, …
 Abstrakte Klassen
 Interfaces
 Anonyme innere Klassen
Polymorphismus
Exceptions
Generics
UML
Java API Dokumentation
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
12
Und jetzt…?
Fortführende Themen wären:
 Weitere Datenstrukturen
 (verkettete) Listen, Stack, Schlangen (Queues), Hash-Tables, Bäume (Trees)
 Frameworks
 Collection-Framework
 Nebenläufigkeit
 Threads, Conditions, Synchronisation, Monitore
 GUI-Programmierung mit Swing / JavaFX
 Design-Pattern
 Algorithmen
 Sortieren & Suchen, Rekursion, …
 U.v.m.
 Einfach am Ball bleiben und viel programmieren ;)
 Den Java-Kurs für Fortgeschrittene im nächsten Semester besuchen…
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
13
Vielen Dank!
Vielen Dank
fürs Zuhören und Mitmachen!
09.02.2017
Javakurs 14: Generics - Lorenz Schauer
14