Th. Letschert OOP 2 5. Generizität I © Th Letschert FH Gießen-Friedberg Th. Letschert OOP 2 Generizität I - Vererbungs- und parametrische Generizität - generische Kollektionen Seite 2 Th. Letschert OOP 2 Vererbungsund parametrische Generizität Seite 3 Generizität Generisch / Generizität unspezifisch, allgemein / allgemein verwendbar Polymorphismus und Generizität generisch (lat.): eine Gattung oder ein Geschlecht (Familie) betreffend. Polymorphismus = Vielgestaltigkeit die Vielgestaltigkeit macht allgemein verwendbar Generizität ist das Ziel: Polymorphismus ist ein Mittel Varianten der Generizität Vererbungs-, Schnittstellen-Polymorphismus ~> VererbungsGenerizität allgemeine Verwendbarkeit duch Typ- / Subtyp-Relation 1. Verebungs-Polymorphismus: » Klasse / Subklasse ~> Typ / Subtyp 2. Schnittstellen-Polymorphismus: » Interface / Implementierende Klasse ~> Typ / Subtyp Parametrischer Polymorphismus ~> Parametrische Generizität allgemeine Verwendbarkeit durch Typ-Parameter Seite 4 Schnittstellen-/Vererbungs- vs. parametrische Generizität Vererbungsgenerizät: Komme mit allen Arten klar ! Parametrische Generizität: Kann für alle Arten etwas erzeugen ! Produziere Zwinger für alle Arten Ich nehme alles, was ein Hund ist. Bernhardiner mit Heidi class Zwinger { }; void sperrEin(Hund h) { ....} ... Zwinger z = new Zwinger(); class Zwinger<T> { void sperrEin(T t) { ...} ... }; Zwinger<Dogge> z = new Zwinger<Dogge>(); Seite 5 Th. Letschert OOP 2 Vererbungs-Generizität Seite 6 Vererbungs-Generizität Einsatz Vererbungsgenerizät I : Abstraktion von Details des Benutzten PinscherZwinger Pinscher hinein(Pinscher): void radau(): void belle(): void DoggenZwinger hinein(Dogge): void radau(): void Vom Pinscher- / Doggen-Zwinger zum Hunde-Zwinger: Für den Hundezwinger ist nur die Hunde-Eigenschaft relevant: ImportSchnittstelle Hund Pinscher Dogge belle(): void belle(): void Dogge belle(): void HundeZwinger hinein(Hund): void radau(): void Hund Hund belle(): void Seite 7 HundeZwinger hinein(Hund): void radau(): void Hund Hund Vererbungs-Generizität Einsatz Vererbungsgenerizität I : Abstraktion von Details des Benutzten public interface Hund { public void belle(); } Pinscher belle(): void Hund public class Pinscher implements Hund { .... } Hund public class Dogge implements Hund { .... } Dogge belle(): void HundeZwinger hinein(Hund): void radau(): void Hund public class HundeZwinger .... } { Import-Schnittstellen werden nicht explizit im Code dokumentiert Seite 8 Vererbungs-Generizität Einsatz Vererbungsgenerizät II : Abstraktion von Implementierungsdetails Vom Array- / Listen-Zwinger zum Zwinger: Für den Benutzer des Zwingers ist die Implementierung irrelevant Export-Schnittstelle Zwinger ArrayZwinger hinein(O): void radau(): void Zwinger ArrayZwinger hinein(O): void radau(): void Array Implementierung mit Array Array Zwinger ListZwinger ListZwinger hinein(O): void radau(): void hinein(O): void radau(): void List List Implementierung mit Liste Seite 9 Vererbungs-Generizität Einsatz Vererbungsgenerizät I / II : 1. Abstraktion von Details des Benutzten 2. Abstraktion von Implementierungsdetails Zwinger ArrayZwinger hinein(Hund): void radau(): void Zwinger ListZwinger hinein(Hund): void radau(): void 2. Hund Hund 1. Seite 10 Kombination: Abstraktion vom Benutzten: Import-Schnittstelle Abstraktion von der Implementierung: Export-Schnittstelle Th. Letschert OOP 2 Parametrische Generizität Seite 11 Parametrische Generizität Einsatz parametrische Generizität : komplette Abstraktion vom Benutzten Erzeugung spezialisierter Varianten Zwinger In einen Zwinger kann man alles sperren: Ein T-Zwinger hat keinen Bezug zu irgendwelchen Eigenschaften von T. T hinein(T): void hinaus(): T Ebene 1: Generic <<bind>>T->Dogge DoggenZwinger hinein(Dogge): void hinaus(): Dogge <<snapshot>> Ebene 2: Klasse käfig:DoggenZwinger Ebene 3: Objekt Seite 12 Parametrische Generizität Parametrische Generizität in Java Zwinger T class Zwinger<T> { private ArrayList<T> l = new ArrayList<T>(); hinein(T): void hinaus(): T Ebene 1: Generic <<bind>>T->Dogge DoggenZwinger } hinein(Dogge): void hinaus(): Dogge public void hinein(T h) { l.add(h); } public void hinaus(T h) { l.remove(0); } Ebene 1/2: Generische Klasse Ebene 2: Klasse Zwinger<Dogge> kaefig = new Zwinger<Dogge>(); käfig:DoggenZwinger <<snapshot>> Ebene 3: Objekt Ebene 3: Objekt Seite 13 Th. Letschert OOP 2 Kombination Parametrische und Vererbungs-Generizität Seite 14 Parametrische- und Vererbungs-Generizität 2 Dimensionen der Generizität Vererbung : Typ ~ Subtyp Parametrisch: Typ-Parameter Vererbung / Schnittstellen-Generizität Hund Zwinger<Bernhardiner> RettungsBernhardiner Seite 15 Zwinger<Dozent> parametrische Generizität Zwinger<Katze> Parametrische + Vererbungs-Generizität Wir können nur Hunde-Zwinger erzeugen. Zwinger benötigt Fähigkeiten von Hund, Beschänkung des Imports Typ-Parameter mit Beschränkungen Hund <<interface>> belle(): void Zwinger class Zwinger<T extends Hund> { private ArrayList<T> l = new ArrayList<T>(); T:Hund hinein(T): void lassBellen(): void } <<bind>>T->Dogge <<snapshot>> public void hinein(T h) { l.add(h); } public void lassBellen() { for(Hund h : l) l.belle(); } Zwinger<Dogge> kaefig = new Zwinger<Dogge>(); käfig:DoggenZwinger Seite 16 Parametrische + Vererbungs-Generizität Typ-Parameter mit Beschränkungen Varianten in der Implementierung Wir können nur Hunde-Zwinger erzeugen. Wir können sie aber in verschiedenen Varianten erzeugen! Hund <<interface>> belle(): void T:Hund Zwinger <<interface>> hinein(T): void lassBellen(): void <<realize>> Das Wesen der Hunde Zwinger beliebiger Hunde beliebiger Implementierung Zwinger beliebiger Hunde mit Listen-Implementierung Doggen-Zwinger beliebiger Implementierung T:Hund ListZwinger hinein(T): void lassBellen(): void Zwinger<Dogge> kaefig = new ListZwinger<Dogge>(); <<bind>>T->Dogge <<snapshot>> käfig:DoggenListZwinger Seite 17 Parametrische + Vererbungs-Generizität Wir können nur Hunde-Zwinger erzeugen. Wir können sie aber in verschiedenen Varianten erzeugen! Typ-Parameter mit Beschränkungen Varianten in der Implementierung Hund <<interface>> belle(): void T:Hund Zwinger <<interface>> hinein(T): void lassBellen(): void interface Hund { public void belle(); } public interface Zwinger<T extends Hund> { public void hinein(T h); public T hinaus() throws NoSuchElementException ; public void lassBellen(); } public class ListZwinger<T extends Hund> implements Zwinger<T> { <<realize>> private List<T> l = new LinkedList<T>(); T:Hund public T hinaus() throws NoSuchElementException{ ... } ListZwinger hinein(T): void lassBellen(): void <<bind>>T->Dogge <<snapshot>> käfig:DoggenListZwinger public void hinein(T h) { ... } public void lassBellen() { ... } } Zwinger<Dogge> kaefig = new ListZwinger<Dogge>(); Seite 18 Th. Letschert OOP 2 Beispiel: Collections-Framework von Java Seite 19 Java-Kollektionen / Collections-Framework Package java.util enthält: Legacy collection classes Standard-Implementierung diverser Kollektionen z.B.: ArrayList, HashMap, ... Collections Framework Framework (Rahmen): Ein System von Klassen (Interfaces) mit Basisfunktionalität Einsatz: Erweiterung durch Entwickler Utility Classes diverse Hilfsklassen Seite 20 Collections-Framework Collections Framework / Interfaces Iterator Iterable ListIterator Collection List Set Iterator und ListIterator: Durchlaufen / Durchlaufen mit erweiterter Funktionalität Queue Map SortedSet SortedMap RandomAccess Kollektionen: Elemente einfügen und mit Iterator durchlaufen Abbildungen: Elemente unter einem Schlüssel einfügen und Zugreifen Wahlfreier Zugriff: Elemente unter einem Index einfügen und Zugreifen Seite 21 Collections-Framework List <<interface>> Collection <<interface>> Map <<interface>> Queue <<interface>> AbstractCollection AbstractList AbstractSet AbstractMap AbstractQueue Abstract SequentialList LinkedList HashSet ArrayList PriorityQueue TreeSet TreeMap Vector HashTable „alte“ Klassen HashMap „alte“ Klassen Stack Properties Seite 22 Th. Letschert OOP 2 Beispiel: Collections-Framework erweitern: Ringpuffer einpassen Seite 23 Ring-Puffer Ring-Puffer: Array ringförmig nutzen buffer[SIZE] front = 0; rear = 0; int count = 0; SIZE = ... Elemente Schreibposition Leseposition Belegte Positionen Puffergrösse Entnehmen Einfügen(x) if (count > 0) v = buf[rear]; rear = (rear + 1) % SIZE; count--; return v; if (count < SIZE) buf[front] = x; count++; front = (front + 1) % SIZE; front rear Seite 24 Ring-Puffer Ring-Puffer Generizität: Dimension Größe Typ der Elemente Anforderungen an den Element-Typ: keine Art Parametrische Generizität, oder Vererbungs-Generizität, oder Parameter des Konstruktors Schnittstelle / Implementierung Ist Ring-Puffer eine Schnittstelle oder eine Implementierung? Seite 25 Ring-Puffer im Collections-Framework Collection <<interface>> List <<interface>> Map <<interface>> Queue <<interface>> AbstractCollection AbstractList AbstractSet AbstractMap Kollektion mit FIFOVerhalten AbstractQueue abstrakte Basisklasse als Implementierungshilfe Abstract SequentialList LinkedList HashSet ArrayList PriorityQueue TreeSet HashMap RingBuffer Vector HashTable „alte“ Klassen „alte“ Klassen Stack Properties Seite 26 TreeMap Ring-Puffer im Collections-Framework Iterable <<interface>> Queue <<interface>> E E AbstractQueue RingBuffer E Ein Ring-Puffer ist eine FIFO-Kolletion mit beschränkter Größe (Interface Queue) Der Typ der Elemente ist unbeschränkter generischer Parameter Die Kapazität kann – als Wert - nur als Parameter des Konstruktors auftreten E Seite 27 Abstract-Queue java.util Class AbstractQueue<E> A Queue implementation that extends this class must minimally define a method Queue.offer(E) which does not permit insertion of null elements, along with methods Queue.peek(), Queue.poll(), Collection.size(), and a Collection.iterator() supporting Iterator.remove(). Typically, additional methods will be overridden as well. If these requirements cannot be met, consider instead subclassing AbstractCollection. Sinn von AbstractQueue: size, offer, poll, peek, und iterator müssen implementiert werden, der Rest basiert auf diesen Methoden und wird von AbstractQueue geliefert. Seite 28 RingBuffer (1) import java.util.AbstractQueue; import java.util.Iterator; public class RingBuffer<E> extends AbstractQueue<E> { private private private private private E int int int int buffer[]; front = 0; rear = 0; count = 0; SIZE; // // // // // Elemente Schreibposition Leseposition Belegte Positionen Puffergroesse public RingBuffer( int capacity ) { SIZE = capacity>0?capacity:0; buffer = (E[]) new Object[SIZE]; } Der Iterator fehlt noch public Iterator<E> iterator() { // TODO implementieren ! } public int size() { return count; } Seite 29 RingBuffer (2) public boolean offer(E x) { assert x != null; if ( count == SIZE ) return false; buffer[front] = x; count++; front = (front + 1) % SIZE; return true; } public E peek() { if (count == 0) return null; return buffer[rear]; } } Ein Element anbieten: Ergebnis ~ hat angenommen Auf das nächste Element sehen, aber es nicht entnehmen public E poll() { if (count == 0) return null; Das nächste Element entnehmen falls E v = buffer[rear]; möglich, sonst null . rear = (rear + 1) % SIZE; count--; return v; } add wird von RingBuffer<String> b = new RingBuffer<String>(3); AbstractQueue b.add("Charlotte"); geliefert. b.add("Emil"); b.add("Hugo"); Exception in thread "main" java.lang.IllegalStateException: Queue full b.add("Karla"); at java.util.AbstractQueue.add(AbstractQueue.java:64) at ringbuffer.RingBuffer.main(RingBuffer.java:55) add-Methode aus AbstractQueue Seite 30 Iterator für RingBuffer (1) public class RingBuffer<E> extends AbstractQueue<E> { ... public Iterator<E> iterator() { return new RingIterator(); } } private class RingIterator implements Iterator<E> { ... } ... Der Iterator wird als private innere Klasse definiert Seite 31 ConcurrentModificationException Wird eine Kollektion verändert während ein Iterator über sie läuft, dann sollte sie eine ConcurrentModificationException werfen Ringpuffer: – Zahl der Modifikationen wird gezählt – Diese Zahl darf sich während eines Iteratorlaufs nicht ändern public class RingBuffer<E> extends AbstractQueue<E> { ... private int modCount = 0; ... public boolean offer(E x) { ... modCount++; ... } Ring-Puffer zählt Modifikationen public E poll() { ... modCount++; ... } } Seite 32 Iterator für RingBuffer (2) private class RingIterator implements Iterator<E> { private int offset = 0; private int modCountAtStart; public RingIterator() { modCountAtStart = modCount; } public boolean hasNext() { if ( modCount != modCountAtStart ) throw new ConcurrentModificationException(); return count-offset > 0; } Modifikation während eines Iteratorlaufs wird entdeckt. public E next() { if (!hasNext()) throw new NoSuchElementException(); E v = buffer[(rear+offset)%SIZE]; public static void main(String[] args){ RingBuffer<String> b = new RingBuffer<String>(6); offset++; b.add("Karla"); return v; for ( String s : b) { System.out.println(s); } } } } public void remove() { throw new UnsupportedOperationException(); } System.out.println( "GEHOLT: "+ b.remove() ) ; Karla GEHOLT: Karla Exception in thread "main" java.util.ConcurrentModificationException at ringbuffer.RingBuffer$RingIterator.hasNext(RingBuffer.java:69) at ringbuffer.RingBuffer.main(RingBuffer.java:89) Seite 33 Th. Letschert OOP 2 Beispiel: Binärbäume und das Collections-Framework Seite 34 Binär-Bäume und das Collections-Framework Fügen Sie geordnete Binär-Bäume in das Collections-Framework ein. Geordnete Binär-Bäume: siehe Datei GBBaum.txt von Prof. Eichner (Algorithmen und Datenstrukturen) Generizität welche Art von Generizität verwendet der Code ? was ist die maximal mögliche Verallgemeinerung ? Charakter (Schnittstelle / abstrakte Klasse / Klasse) ? Einpassen entsprechend der Java-Konventionen ! Seite 35 Binär-Bäume und das Collections-Framework Iterable <<interface>> E Jede Kollektion ist iterierbar. Set <<interface>> E Jede Implementierung muss das Interface Set implementieren. Mengen haben beliebige Elemente. AbstractSet GbbSet E AbstractSet ist eine Hilfsklasse für die Implementierung. E:Comparable<E> add(E e):boolean remove(Object o):boolean E Processor <<interface>> process(E e) GbbSet basiert auf geordneten binären Bäumen und kann darum nur vergleichbare Elemente verwalten. Die Traversierung eines Baums kann mit eine beliebige Aktion auf den Elementen verbunden werden. E:Comapable<E> GBBaum ... traverse(Processor<E> p) Menge mit Binärbaum-Implementierung Seite 36