Programmierkurs Java Teil Objektorientierte Programmierung Unterrichtseinheit 38 Generics

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 White. Wine Red. Wine Ø 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 get. Brewery() public String to. String() { this. brewery = brewery; } { return this. get. Class(). get. Name() + "[" + this. brewery + "]“; } } abstract class Wine extends Drink { private String region; public Wine(String region) { this. region = region; } public String get. Region() { return this. region; } public String to. String() { return this. get. Class(). get. Name() + "[" + this. region + "]"; } } class White. Wine extends Wine { public White. Wine(String region) } { super(region); } class Red. Wine extends Wine { public Red. Wine(String region) } { super(region); } Programmierkurs Java UE 38 Generics Dietrich Boles Seite 4

Motivation / erste Lösung class Bottle { private Drink content; public boolean is. Empty() { 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 beer. Bottle = new Bottle(); beer. Bottle. fill(new White. Wine("Burgunder")); . . . Beer beer = (Beer)beer. Bottle. empty(); // Class. Cast. Exception! Programmierkurs Java UE 38 Generics Dietrich Boles Seite 5

Motivation / zweite Lösung abstract class Drink. Bottle {} class Beer. Bottle extends Drink. Bottle { 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 Wine. Bottle extends Drink. Bottle {} class White. Wine. Bottle extends Wine. Bottle { private White. Wine cont; public void fill(White. Wine content) { this. cont = content; } public White. Wine empty() { White. Wine c=this. cont; this. cont=null; return c; } } class Red. Wine. Bottle extends Wine. Bottle { private Red. Wine cont; public void fill(Red. Wine content) { this. cont = content; } public Red. Wine empty() { Red. Wine 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 is. Empty() { 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> beer. Bottle = new Bottle<Beer>(); Bottle<White. Wine> white. Wine. Bottle = new Bottle<White. Wine>(); Aktueller Typ-Parameter (Klasse) beer. Bottle. fill(new Beer("Veltins")); beer. Bottle. fill(new White. Wine("Burgunder")); // Fehlermeldung durch Compiler! white. Wine. Bottle. fill(new White. Wine("Burgunder")); Beer beer = beer. Bottle. empty (); System. out. println (beer); White. Wine white. Wine = white. Wine. Bottle. empty (); System. out. println (white. Wine); 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<T 1, T 2> { public void setze(T 1 obj 1, T 2 obj 2); } class F<T 4, T 5, T 6> implements I<T 4, T 6> { public T 5 liefere() {. . . } public void setze(T 4 obj 1, T 6 obj 2) {. . . } } 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> obj 2 = new C<B>(); A a = obj. f(new A()); B b = obj 2. f(new B()); A a 2 = 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 t 1 = String return } } { { t; this. t = t 1; str = t. to. String(); this. t; Compiler class C { Object t; Object f(Object t) { Object t 1 = t; this. t = t 1; String str = t. to. String(); 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 C<T> {. . . } A { } B { } D extends A { } C<B> C<A> C<D> 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 has. Next(); 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. Abstract. List<T> implements Iterable<T>, . . . { public Vector(); public Vector(int init. Capacity); public boolean add(T obj); public boolean contains(Object obj); public T element. At(int index); public T get(int index); public void insert. Element. At(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 Iterator. Bsp. Mit. Cast { 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. has. Next()) { 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 Iterator. Bsp { 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. has. Next()) { 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 Iterator. Bsp. For { 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; } System. out. println(summe); neue for-Schleife } } 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> drink. Bottle = new Bottle<Drink>(); Bottle<Beer> beer. Bottle = new Bottle<Beer>(); Bottle<Object> object. Bottle = new Bottle<Object>(); // Fehlermeldung durch Compiler Bottle<Petrol> petrol. Bottle = new Bottle<Petrol>(); // Fehlermeldung durch Compiler Bottle<Stout> stout. Bottle = new Bottle<Stout>(); Programmierkurs Java UE 38 Generics Dietrich Boles Seite 18

Typ-Parameter mit Einschränkungen / Übersetzung class A { public void do. It() {. . . } } class C<T extends A> { T t; T f(T t) { T t 1 = t; this. t = t 1; this. t. do. It(); return this. t; } } Programmierkurs Java Compiler UE 38 Generics class C { A t; A f(A t) { A t 1 = t; this. t = t 1; this. t. do. It(); 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<T 1 extends A, T 2 extends B> { public void f 1(T 1 t); public void f 2(T 2 t); } class C implements I<A, B> { public void f 1(A t) {. . . } public void f 2(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 Bottle. Box<T extends Drink> { private Object[] bottles; private int count = 0; public Bottle. Box(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> get. Bottle(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) Bottle. Box<Beer> beer. Bottle. Box = new Bottle. Box<Beer>(6); // Bierkasten füllen for (int i = 0; i < 6; i++) { Bottle<Beer> beer. Bottle = new Bottle<Beer>(); beer. Bottle. fill(new Beer("Jever")); beer. Bottle. Box. add(beer. Bottle); } Bottle<Red. Wine> wine. Bottle = new Bottle<Red. Wine>(); beer. Bottle. Box. add(wine. Bottle); // Fehlermeldung. . . // Bierkasten leeren for (int i = 0; i < 6; i++) { Bottle<Beer> beer. Bottle = beer. Bottle. Box. get. Bottle(i); Beer beer = beer. Bottle. 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 Bottle. Box { private Object[] bottles; private int count = 0; public Bottle. Box(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> get. Bottle(int index) { return (Bottle<? extends Drink>)this. bottles[index]; } } Programmierkurs Java UE 38 Generics Dietrich Boles Seite 24

Wildcards (2) Bottle. Box box = new Bottle. Box(6); // Füllen Bottle<Beer> beer. Bottle = new Bottle<Beer>(); beer. Bottle. fill(new Beer("Veltins")); box. add (beer. Bottle); Bottle<White. Wine> white. Wine. Bottle = new Bottle<White. Wine>(); white. Wine. Bottle. fill (new White. Wine("Burgunder")); box. add (white. Wine. Bottle); // Leeren for (int i = 0; i < 6; i++) { Bottle<? extends Drink> bottle = box. get. Bottle (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<? > c 1 = new C<A>(); C<? > c 2 = new C<B>(); // 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: Bottle. Box<Beer> Ø Lösung: Semantische Ungereimtheit class Bottle. Box<T extends Bottle<? extends Drink>> { private Object[] bottles; private int count = 0; public Bottle. Box(int number) { this. bottles = new Object[number]; } public void add(T bottle) { this. bottles[this. count] = bottle; this. count++; } public T get. Bottle(int index) { return (T)this. bottles[index]; } } Programmierkurs Java UE 38 Generics Dietrich Boles Seite 27

Wildcards (5) Bottle. Box<Bottle<Beer>> beer. Bottle. Box = new Bottle. Box<Bottle<Beer>>(6); // Bierkasten füllen for (int i = 0; i < 6; i++) { Bottle<Beer> beer. Bottle = new Bottle<Beer>(); beer. Bottle. fill(new Beer("Jever")); beer. Bottle. Box. add(beer. Bottle); }. . . // Bierkasten leeren for (int i = 0; i < 6; i++) { Bottle<Beer> beer. Bottle = beer. Bottle. Box. get. Bottle(i); Beer beer = beer. Bottle. 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 Bottle. Transfuser<T extends Drink> { void transfuse(Bottle<T> from. Bottle, Bottle<T> to. Bottle) { T drink = from. Bottle. empty(); to. Bottle. fill(drink); } } Bottle. Transfuser<Beer> beer. Transfuser = new Bottle. Transfuser<Beer>(); . . . Ø Problem: beer. Transfuser kann nur Bierflaschen umfüllen! Programmierkurs Java UE 38 Generics Dietrich Boles Seite 31

Generische Methoden / Motivation (2) Ø allgemeiner Lösung: Generische Methoden class Bottle. Transfuser { <T extends Drink> void transfuse (Bottle<T> from. Bottle, Bottle<T> to. Bottle) { T drink = from. Bottle. empty(); to. Bottle. fill(drink); } } Bottle. Transfuser transfuser = new Bottle. Transfuser (); Bottle<Beer> b 1 = new Bottle<Beer>(); b 1. fill(new Beer("Jever")); Bottle<Beer> b 2 = new Bottle<Beer>(); transfuser. transfuse(b 1, b 2); Bottle<White. Wine> b 3 = new Bottle<White. Wine>(); b 3. fill(new White. Wine("Burgunder")); Bottle<White. Wine> b 4 = new Bottle<White. Wine>(); transfuser. transfuse(b 3, b 4); transfuser. transfuse(b 1, b 4); // 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 f 1(T t) { String str = t. to. String(); } public static <T extends A> void f 2(T t) { t. f(); } } C obj = new C(); obj. f 1("Hallo"); obj. f 1(4711); C. f 2(new A()); C. f 2(new B()); C. f 2("Hallo"); // Fehlermeldung Programmierkurs Java UE 38 Generics Dietrich Boles Seite 33

Generische Methoden / Beispiele (2) class C { public static <T> void f 3(T t 1, T t 2) { } public static <T> T f 4(T t 1, T t 2) { return Math. random() < 0. 5 ? t 1 : t 2; } Die "T" müssen } einen gemeinsamen Supertyp haben C. f 3(11, "Hallo"); // Supertyp Object C. f 3("Hallo", 11); // Supertyp Object String s 2 = C. f 4("Hallo", "World"); // Supertyp String s 1 = C. f 4(11, "Hallo"); // Fehlermeldung Comparable c 1 = C. f 4(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 Class. Cast-Exceptions Programmierkurs Java UE 38 Generics Dietrich Boles Seite 35
- Slides: 35