Programmierkurs Java

Werbung
Programmierkurs Java
Teil
Objektorientierte Programmierung
Unterrichtseinheit 38
Generics
Dr. Dietrich Boles
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 1
Gliederung
 Generics
 Motivation



 Generische Klassen

 Klassendefinition

 Objekte / Objektvariablen 
 Übersetzung

 Typ-Kompatibilität
 Generische JDK-Klassen
 Iterator
 Vector
 Beispiel
Typ-Parameter mit Einschränkungen
Vererbung
Interfaces
Arrays
Wildcards
new
static
 Generische Methoden
 Motivation
 Beispiele
 Zusammenfassung
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 2
Motivation / Problem
 Simulation eines Getränkehandels
 Quelle: J. Nowak. Fortgeschrittene Programmierung mit Java 5,dpunkt.
 Gegeben: vielerlei Getränke
Drink
Wine
Beer
WhiteWine
RedWine
 Gewünscht: typisierte Flaschen, die nur mit Bier, Rotwein oder
Weißwein gefüllt werden können
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 3
Motivation / Ausgangslage
abstract class Drink { }
class Beer extends Drink {
private String brewery;
public Beer(String brewery)
public String getBrewery()
public String toString()
{ this.brewery = brewery; }
{ return this.brewery; }
{ return this.getClass().getName() +
"[" + this.brewery + "]“; }
}
abstract class Wine extends Drink {
private String region;
public Wine(String region)
{ this. region = region; }
public String getRegion()
{ return this.region; }
public String toString()
{ return this.getClass().getName() +
"[" + this.region + "]"; }
}
class WhiteWine extends Wine {
public WhiteWine(String region)
}
{ super(region); }
class RedWine extends Wine {
public RedWine(String region)
}
{ super(region); }
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 4
Motivation / erste Lösung
class Bottle {
private Drink content;
public boolean isEmpty() {
return this.content == null;
}
public void fill(Drink content) {
this.content = content;
}
public Drink empty() {
Lösung: polymorphe Klasse
Drink content = this.content;
Probleme:
this.content = null;
- keine korrekte Lösung
return content;
- Typ-Unsicherheit
}
}
...
Bottle beerBottle = new Bottle();
beerBottle.fill(new WhiteWine("Burgunder"));
...
Beer beer = (Beer)beerBottle.empty(); // ClassCastException!
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 5
Motivation / zweite Lösung
abstract class DrinkBottle {}
class BeerBottle extends DrinkBottle {
private Beer cont;
public void fill(Beer content) { this.cont = content; }
public Beer empty() { Beer c=this.cont; this.cont=null; return c; }
}
abstract class WineBottle extends DrinkBottle {}
class WhiteWineBottle extends WineBottle {
private WhiteWine cont;
public void fill(WhiteWine content) { this.cont = content; }
public WhiteWine empty() { WhiteWine c=this.cont; this.cont=null; return c; }
}
class RedWineBottle extends WineBottle {
private RedWine cont;
public void fill(RedWine content) { this.cont = content; }
public RedWine empty() { RedWine c = this.cont; this.cont = null; return c; }
}
Lösung:
Problem:
Getränk-spezifische Flaschen
Keine Vererbung, keine wirkliche Polymorphie!
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 6
Motivation / dritte Lösung (1)
class Bottle<T> {
private T content;
public boolean isEmpty() { return this.content == null; }
public void fill (T content) { this.content = content; }
public T empty() {
T content = this.content;
this.content = null;
return content;
}
}
Lösung:
- Generische (parametrisierte) Klasse
- T ist formaler Typ-Parameter (Typ-Variable)
der Klasse Bottle
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 7
Motivation / dritte Lösung (2)
Bottle<Beer> beerBottle = new Bottle<Beer>();
Bottle<WhiteWine> whiteWineBottle = new Bottle<WhiteWine>();
Aktueller Typ-Parameter (Klasse)
beerBottle.fill(new Beer("Veltins"));
beerBottle.fill(new WhiteWine("Burgunder"));
// Fehlermeldung durch Compiler!
whiteWineBottle.fill(new WhiteWine("Burgunder"));
Beer beer = beerBottle.empty ();
System.out.println (beer);
WhiteWine whiteWine = whiteWineBottle.empty ();
System.out.println (whiteWine);
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 8
Generische Klassen / Klassendefinition
class C<T>
{ ... }
T kann im weiteren Klassenkopf und im Klassenrumpf
(fast) überall da verwendet werden, wo Klassennamen
stehen können
class D<T> extends T { }
class E<T> { T obj; }
interface I<T1, T2> {
public void setze(T1 obj1, T2 obj2);
}
class F<T4, T5, T6> implements I<T4, T6> {
public T5 liefere() { ...}
public void setze(T4 obj1, T6 obj2) { ... }
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 9
Generische Klassen / Objekte und Objektvariablen
class C<T> {
Formaler Typ-Parameter
public T f(T t) {...}
}
class A { }
class B { }
Parametrisierte Klasse / Typ
C<A> obj = new C<A>();
Aktueller Typ-Parameter (Klasse, kein Standarddatentyp)
C<B> obj2 = new C<B>();
A a = obj.f(new A());
B b = obj2.f(new B());
A a2 = obj.f(new B()); // Fehlermeldung durch Compiler
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 10
Generische Klassen / Übersetzung
class C<T>
T t;
T f(T t)
T t1 =
String
return
}
}
{
{
t; this.t = t1;
str = t.toString();
this.t;
Compiler
class C {
Object t;
Object f(Object t) {
Object t1 = t; this.t = t1;
String str = t.toString();
return this.t;
}
}
Raw-Type
Daher erlaubt:
C obj = new C();
+ Meta-Informationen!
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 11
Generische Klassen / Typ-Kompatibilität
class
class
class
class
C<T> { ... }
A { }
B { }
D extends A { }
C<B>  C<A>
C<D>  C<A>
C<A>  C<Object>
// C<A> obj = new C<B>(); Fehler
// C<A> obj = new C<D>(); Fehler
// C<Object> = new C<A>(); Fehler
C<A>  Object
// Object obj = new C<A>(); ok
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 12
Generische JDK-Klassen / java.util.Iterator
public interface Iterator<T> {
public boolean hasNext();
public T next();
}
public interface Iterable<T> { // in java.lang
public java.util.Iterator<T> iterator();
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 13
Generische JDK-Klassen / java.util.Vector
public class Vector<T>
extends java.util.AbstractList<T>
implements Iterable<T>, ... {
public Vector();
public Vector(int initCapacity);
public boolean add(T obj);
public boolean contains(Object obj);
public T elementAt(int index);
public T get(int index);
public void insertElementAt(T o,int i);
public boolean remove(Object obj);
public int size();
public java.util.Iterator<T> iterator();
...
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 14
Generische JDK-Klassen / Beispiel
import java.util.Vector;
import java.util.Iterator;
public class IteratorBspMitCast {
public static void main(String[] args) {
Vector speicher = new Vector();
speicher.add(4711);
speicher.add(46);
speicher.add(33);
Raw-Type
int summe = 0;
Iterator iter = speicher.iterator();
while (iter.hasNext()) {
summe += (Integer)iter.next();
}
Cast notwendig!
System.out.println(summe);
}
Compiler-Warnung:
... uses unsafe operations
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 15
Generische JDK-Klassen / Beispiel
import java.util.Vector;
import java.util.Iterator;
public class IteratorBsp {
public static void main(String[] args) {
Vector<Integer> speicher = new Vector<Integer>();
speicher.add(4711);
speicher.add(46);
speicher.add(33);
int summe = 0;
Iterator<Integer> iter = speicher.iterator();
while (iter.hasNext()) {
summe += iter.next();
Kein Cast notwendig!
}
System.out.println(summe);
}
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 16
Generische JDK-Klassen / Beispiel
import java.util.Vector;
public class IteratorBspFor {
public static void main(String[] args) {
Vector<Integer> speicher = new Vector<Integer>();
speicher.add(4711);
speicher.add(46);
speicher.add(33);
int summe = 0;
for (Integer i : speicher) {
summe += i;
}
neue for-Schleife
System.out.println(summe);
}
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 17
Typ-Parameter mit Einschränkungen
 Motivationsproblem: Bottle<Object> ist keine Getränkeflasche!
 Lösung: Einschränkung des Typ-Parameters
class Bottle<T extends Drink> { /* wie auf Folie 7 */ }
class Petrol { }
class Stout extends Beer { ... }
Bottle<Drink> drinkBottle = new Bottle<Drink>();
Bottle<Beer> beerBottle = new Bottle<Beer>();
Bottle<Object> objectBottle = new Bottle<Object>();
// Fehlermeldung durch Compiler
Bottle<Petrol> petrolBottle = new Bottle<Petrol>();
// Fehlermeldung durch Compiler
Bottle<Stout> stoutBottle = new Bottle<Stout>();
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 18
Typ-Parameter mit Einschränkungen / Übersetzung
class A {
public void doIt() {...}
}
class C<T extends A> {
T t;
T f(T t) {
T t1 = t;
this.t = t1;
this.t.doIt();
return this.t;
}
}
Programmierkurs Java
Compiler
UE 38
Generics
class C {
A t;
A f(A t) {
A t1 = t;
this.t = t1;
this.t.doIt();
return this.t;
}
}
Dietrich Boles
Seite 19
Vererbung
class A { }
class B<T extends A> {
public void f(T t) { ... }
}
class C<T> extends A { }
class D<T extends A> extends B<T> { }
class E extends B<A> {
public void f(A obj) { ... }
public void f(Object obj) { ... }
}
Programmierkurs Java
UE 38
// Überschreiben
// Überladen
Generics
Dietrich Boles
Seite 20
Interfaces
class A { }
class B { }
interface I<T1 extends A, T2 extends B> {
public void f1(T1 t);
public void f2(T2 t);
}
class C implements I<A, B> {
public void f1(A t) { ... }
public void f2(B t) { ... }
}
class D implements I<A, A>, I<B, B> { ... }
// Fehler: dasselbe Interface nicht zweimal implementieren
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 21
Arrays mit parametrisierten Klassen (1)
 Motivationsproblem: Klasse für Getränke-spezifische Getränkekästen
 Problem: Arrays mit parametrisierten Klassen sind nicht erlaubt
 Lösung:
class BottleBox<T extends Drink> {
private Object[] bottles; private int count = 0;
public BottleBox(int number) {
// this.bottles = new T[number]; nicht erlaubt
this.bottles = new Object[number];
}
public void add(Bottle<T> bottle) {
this.bottles[this.count] = bottle; this.count++;
}
public Bottle<T> getBottle(int index) {
return (Bottle<T>)this.bottles[index];
}
}
Internes sicheres (!) Cast
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 22
Arrays mit parametrisierten Klassen (2)
BottleBox<Beer> beerBottleBox = new BottleBox<Beer>(6);
// Bierkasten füllen
for (int i = 0; i < 6; i++) {
Bottle<Beer> beerBottle = new Bottle<Beer>();
beerBottle.fill(new Beer("Jever"));
beerBottleBox.add(beerBottle);
}
Bottle<RedWine> wineBottle = new Bottle<RedWine>();
beerBottleBox.add(wineBottle); // Fehlermeldung
...
// Bierkasten leeren
for (int i = 0; i < 6; i++) {
Bottle<Beer> beerBottle = beerBottleBox.getBottle(i);
Beer beer = beerBottle.empty ();
System.out.println(beer);
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 23
Wildcards (1)
 Motivationsproblem: Klasse für Getränkekästen mit beliebigen Flaschen
 Lösung: „Wildcards“
class BottleBox {
private Object[] bottles;
private int count = 0;
public BottleBox(int number) {
this.bottles = new Object[number];
}
public void add(Bottle<? extends Drink> bottle) {
this.bottles[this.count] = bottle; this.count++;
}
public Bottle<? extends Drink> getBottle(int index) {
return (Bottle<? extends Drink>)this.bottles[index];
}
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 24
Wildcards (2)
BottleBox box = new BottleBox(6);
// Füllen
Bottle<Beer> beerBottle = new Bottle<Beer>();
beerBottle.fill(new Beer("Veltins"));
box.add (beerBottle);
Bottle<WhiteWine> whiteWineBottle = new Bottle<WhiteWine>();
whiteWineBottle.fill (new WhiteWine("Burgunder"));
box.add (whiteWineBottle);
// Leeren
for (int i = 0; i < 6; i++) {
Bottle<? extends Drink> bottle = box.getBottle (i);
Drink drink = bottle.empty ();
System.out.println(drink);
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 25
Wildcards (3)
class A { }
class B { }
class C<T> { }
C<A> ca = new C<A>();
// ok
C<Object> cobj = new C<A>(); // Fehler
C obj = new C<A>();
// ok, aber möglichst vermeiden
C<?> c1 = new C<A>();
C<?> c2 = new C<B>();
// ok
// ok
? steht für irgendeinen Typ
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 26
Wildcards (4)
 Motivationsproblem: Klasse für Getränke-spezifische Getränkekästen
 Problem der Lösung auf Folie 22: BottleBox<Beer>
 Lösung:
Semantische Ungereimtheit
class BottleBox<T extends Bottle<? extends Drink>> {
private Object[] bottles; private int count = 0;
public BottleBox(int number) {
this.bottles = new Object[number];
}
public void add(T bottle) {
this.bottles[this.count] = bottle; this.count++;
}
public T getBottle(int index) {
return (T)this.bottles[index];
}
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 27
Wildcards (5)
BottleBox<Bottle<Beer>> beerBottleBox =
new BottleBox<Bottle<Beer>>(6);
// Bierkasten füllen
for (int i = 0; i < 6; i++) {
Bottle<Beer> beerBottle = new Bottle<Beer>();
beerBottle.fill(new Beer("Jever"));
beerBottleBox.add(beerBottle);
}
...
// Bierkasten leeren
for (int i = 0; i < 6; i++) {
Bottle<Beer> beerBottle = beerBottleBox.getBottle(i);
Beer beer = beerBottle.empty ();
System.out.println(beer);
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 28
new
class A { }
class C<T> {
T t = new T();
// Fehlermeldung; Grund: Übersetzung
// von T nach Object
}
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 29
static
class A { }
Class B { }
class C<T> {
static T t;
// Fehlermeldung
static C<T> c; // Fehlermeldung
}
C<A> ca = new C<A>();
C<B> cb = new C<B>();
Grund: Der Compiler generiert nur eine (!) Klasse.
Beide Objekte würden sich die statischen Attribute teilen.
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 30
Generische Methoden / Motivation (1)
 Motivationsproblem: Umfüllen von Flaschen
 Mögliche Lösung:
class BottleTransfuser<T extends Drink> {
void transfuse(Bottle<T> fromBottle,
Bottle<T> toBottle) {
T drink = fromBottle.empty();
toBottle.fill(drink);
}
}
BottleTransfuser<Beer> beerTransfuser = new
BottleTransfuser<Beer>();
...
 Problem: beerTransfuser kann nur Bierflaschen umfüllen!
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 31
Generische Methoden / Motivation (2)
 allgemeiner Lösung: Generische Methoden
class BottleTransfuser {
<T extends Drink> void transfuse (Bottle<T> fromBottle,
Bottle<T> toBottle) {
T drink = fromBottle.empty();
toBottle.fill(drink);
}
}
BottleTransfuser transfuser = new BottleTransfuser ();
Bottle<Beer> b1 = new Bottle<Beer>(); b1.fill(new Beer("Jever"));
Bottle<Beer> b2 = new Bottle<Beer>();
transfuser.transfuse(b1, b2);
Bottle<WhiteWine> b3 = new Bottle<WhiteWine>();
b3.fill(new WhiteWine("Burgunder"));
Bottle<WhiteWine> b4 = new Bottle<WhiteWine>();
transfuser.transfuse(b3, b4);
transfuser.transfuse(b1, b4); // Fehlermeldung
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 32
Generische Methoden / Beispiele (1)
class A { public void f() {} }
class B extends A { }
class C {
public <T> void f1(T t) {
String str = t.toString();
}
public static <T extends A> void f2(T t) {
t.f();
}
}
C obj = new C();
obj.f1("Hallo");
obj.f1(4711);
C.f2(new A());
C.f2(new B());
C.f2("Hallo"); // Fehlermeldung
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 33
Generische Methoden / Beispiele (2)
class C {
public static <T> void f3(T t1, T t2) { }
public static <T> T f4(T t1, T t2) {
return Math.random() < 0.5 ? t1 : t2;
}
Die "T" müssen
}
einen gemeinsamen Supertyp haben
C.f3(11, "Hallo"); // Supertyp Object
C.f3("Hallo", 11); // Supertyp Object
String s2 = C.f4("Hallo", "World"); // Supertyp String
String s1 = C.f4(11, "Hallo"); // Fehlermeldung
Comparable c1 = C.f4(11, "World"); // Supertyp Comparable
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 34
Zusammenfassung
 Generics: Parametrisierte Klassen
 Sinn und Zweck:
 Semantische Korrektheit
 Vermeidung von Type-Casts
 Vermeidung von ClassCast-Exceptions
Programmierkurs Java
UE 38
Generics
Dietrich Boles
Seite 35
Herunterladen