slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Grundlagen der Informatik I Thema 18: Typkonvertierungen und Generizität PowerPoint Presentation
Download Presentation
Grundlagen der Informatik I Thema 18: Typkonvertierungen und Generizität

Loading in 2 Seconds...

play fullscreen
1 / 34

Grundlagen der Informatik I Thema 18: Typkonvertierungen und Generizität - PowerPoint PPT Presentation


  • 82 Views
  • Uploaded on

Grundlagen der Informatik I Thema 18: Typkonvertierungen und Generizität. Prof. Dr. Max Mühlhäuser Dr. Guido Rößling. Typkonvertierung. Mit statischer Typisierung gibt es viele Kontexte, in denen Werte eines bestimmten Typs erwartet werden.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Grundlagen der Informatik I Thema 18: Typkonvertierungen und Generizität' - abeni


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide1

Grundlagen der Informatik IThema 18: Typkonvertierungen und Generizität

Prof. Dr. Max Mühlhäuser

Dr. Guido Rößling

typkonvertierung
Typkonvertierung
  • Mit statischer Typisierung gibt es viele Kontexte, in denen Werte eines bestimmten Typs erwartet werden.
    • In “a = expression” erwarten wir, dass expression den gleichen Typ wie a hat
    • In “a + b” erwarten wir, dass a und b entweder beide ganze Zahlen, Fließkommazahlen oder Zeichenketten sind.
    • In “f(a,b)” erwarten wir, dass die Typen der Argumente sich mit denen der formalen Parameter decken.
arten der typkonvertierung in java
Arten der Typkonvertierung in Java
  • Identitätskonvertierung – von einem Typ auf denselben Typ
    • Es ist z.B. ok, eine redundante Konvertierung einzufügen
  • Verbreiterung primitiver Typen
    • z.B. byte nach int (ohne Informationsverlust)
    • z.B. int nach double (möglicher Informationsverlust)
  • Verengung primitiver Typen
    • z.B. int nach byte (höhere Bits wegwerfen)
    • z.B. float nach int
arten der typkonvertierung in java1
Arten der Typkonvertierung in Java
  • Verbreiterung von Referenztypen (Verallgemeinerung)
    • Gegeben Referenztypen A, B, dann kann A zu B verbreitert werden genau dann wenn A ein Subtyp vonB ist.
    • Diese Konvertierung geschieht immer während der Kompilierung und kann keine Laufzeitfehler auslösen.
  • Verengung von Referenztypen (Konkretisierung)
    • Gegeben Referenztypen A, B, dann kann A verengt werden zu B genau dann wenn B ein Subtyp von A ist.
    • Diese Konvertierung muss zur Laufzeit überprüft werden und löst möglicherweise einen Laufzeitfehler aus.
arten der typkonvertierung in java2
Arten der Typkonvertierung in Java
  • String Konvertierung
    • Jeder Typ kann zu String konvertiert werden
    • Implizit wird die toString()-Methode (aus java.lang.Object) benutzt
    • Das haben Sie z.B. bei System.out.println(myObject)schon genutzt
  • Boxing und Unboxing
    • Von byte zu Byte, int zu Integer etc. und umgekehrt
  • Ungeprüfte Konvertierung
    • Konvertierungen die zu einem Fehler führen können, ergeben Warnung zur Kompilierzeit
    • z.B. Konvertierung eines “raw type” zu einem parametrisierten Typ
konvertierungskontexte
Konvertierungskontexte
  • Zuweisungskonvertierung:v = expr
    • Konvertiere den Typ von expr zum Typ von v.
    • Nur für Konvertierungen ohne mögliche Laufzeitfehler
  • Methodenaufrufkonvertierung:expr.M(expr‘)
    • konvertiert die Typen der Argumente.
    • Nur für Konvertierungen ohne mögliche Laufzeitfehler
  • Cast-Konvertierung:(T)expr
    • Konvertiert den Typ von exprzu T
    • Kann auch die Verengungskonvertierung benutzen!
  • Numerische Konvertierung
    • Konvertiert die Operanden eines numerischen Operators in einen gemeinsamen Typen, so dass die Operation ausgeführt werden kann
      • erlaubt Identitäts-, Verbreiterungs- und Unboxing Konvertierung
      • 4 + 2.0  6.0
typ verengung
Typ-Verengung
  • Verbreiterung verliert statische Typinformation

// p bezieht sich auf Circle, Verbreiterung OKGraphicObject p = new Circle(5, 12, 4); Circle c1 = p; // compile-Fehler — kann nicht verengen

  • Die Information kann zur Laufzeit mit Tests und Casts wieder zurückgeholt werden kann

if (p instanceof Circle) { // Laufzeittest c1 = (Circle) p; // Explizite Verengung} // zur Laufzeit OK

nochmals collections
Nochmals Collections…
  • Eine Collection sollte zu mehr als einem Typ passen
    • Eine Implementierung langt für verschiedene Zwecke
    • Das allgemeine Verhalten der Collection hängt nicht vom Elementtyp ab
  • Zusätzlich wollen wir einen spezifischenElementtypgarantiert haben
    • Angenommen, wir nutzen eine Collection nur für Person Instanzen
    • Dann wollen wir auch Person Objekte ohne Verendung von Objecterhalten können!
nochmals collections1
Nochmals Collections…

List myIntList = newLinkedList(); // 1

myIntList.add(new Integer(0)); // 2

Integer x = (Integer)myIntList.get(0); // 3

  • Der Cast in Zeile ist nervig aber erforderlich
    • Java garantiert nur, dass das Ergebnis zum Typ Object passt
    • Es kann also vom Typ Integer sein – oder eben auch nicht!
      • Daher kriegen wir einen Typfehler ohne den Cast
  • Zudem wurde der Cast nicht mit instanceofabgesichert
  • Wir könnten uns auch irren und etwas anderes erhalten

List myIntList = newLinkedList(); // 1

myIntList.add("Hello World"); // 2’

Integer x = (Integer)myIntList.get(0); // 3

    • Nun erhalten wir eine ClassCastException zur Laufzeit!
angabe der absichten mit generics
Angabe der Absichten mit Generics
  • Wir müssen Java mehr über unsere Absichten sagen
    • “Diese Liste will nur Elemente haben, die zu Integer passen”
  • Dafür müssen wir…:
    • Die Deklaration der Liste anpassen: List<Integer>
    • Das Anlegen der Liste anpassen: newLinkedList<Integer>
  • Vorteile:
    • Wir können auf den Cast in Zeile 3 verzichten
    • Typen, die nicht zu Integer passen, können nicht eingefügt oder abgefragt werden

List<Integer> myIntList = newLinkedList<Integer>(); // 1

myIntList.add(new Integer(0)); // 2

Integer x = myIntList.get(0); // 3

myIntList.add("Hello World"); //compile-Fehler

was hat sich ge ndert
Was hat sich geändert?
  • myIntList ist nun eine List<Integer>
    • Nicht „eine“ Liste, sondern eine Liste von Integer-Objekten
    • Wir müssen Ergebnisse also nicht mehr nach Integercasten
  • Wir können keine anderen Typen mehr speichern
  • Darum kümmern sich der Java Compiler (und Eclipse)
  • myIntList.add("Hello World");
  • The method add(Integer)in the type List<Integer> is not applicable for the arguments (String)
und was bringt uns das
Und was bringt uns das?
  • Auf den ersten Blick vielleicht nicht viel
    • Die Deklaration und Erzeugung der Liste sind länger
    • Wir können nur auf den Cast verzichten
  • Es gibt aber einen großen Unterschied!
    • Nun kann der Compiler die Typkorrektheit überprüfen
    • Die Deklaration von myIntList gibt den Typ an
    • Der Compiler stellt sicher, dass alle Zugriffe zum Typ passen
  • Was ist hier anders als bei einem Cast?
    • Mit einem Cast sagt der Programmierer “der Typ sollte hier zum Cast zu Typ X passen”
      • Das sollte immer mit instanceofabgesichert werden!
    • Generische Deklaration sagt “das passt immer zu Typ X“
nochmals listen
Nochmals Listen

List und Iterator werden in java.util wie folgt definiert:

public interface List<E> {

void add(E x);

Iterator<E> iterator();

}

publicinterface Iterator<E> {

E next();

boolean hasNext();

void remove();

}

  • Das kennen wir im Wesentlichen schon (siehe T15)
    • Außer dem <E> und E
  • Damit wird ein formaler Typparameterdeklariert
  • E kann dann in der Klasse als Typ genutzt werden
list integer und integerlist
List<Integer> und IntegerList
  • Wir nutzen List<Integer>, aber es gibt nur List<E>?
  • E ist ein formaler Typparameter
    • Ähnlich zu den formalen Parametern in Methodensignaturen
  • E wird in allen Aufrufen durch Integer ersetzt
  • Intuitiv kann man sich das so vorstellen:

publicinterfaceIntegerList {

voidadd(Integer x);

IntegerIteratoriterator();

}

  • Das kann das Verständnis erleichtern
  • Aber es ist auch irreführend
    • Es gibt nur eine List Klasse, nicht “eine pro Typ”
generics und subtypen
Generics und Subtypen
  • Warum ist der folgende Code nicht korrekt?
  • Zeile 1 ist sicherlich korrekt
    • LinkedList ist ein Untertyp von List
    • Die formalen Parameter (beide String) „passen“
  • Passt List<String> in Zeile 2 zu List<Object>?
    • List ist auf beiden Seiten die gleiche Basisklasse
    • String ist ein Erbe von Object
    • Die intuitive Antwort ist also „ja”

List<String> ls = newLinkedList<String>(); // 1

List<Object> lo = ls; // 2

lo.add(newObject()); // 3

String s = ls.get(0); // 4

Dieser Code wird vom Übersetzer so nicht akzeptiert!

generics und subtypen1
Generics und Subtypen
  • Zunächst schauen wir uns den weiteren Code an:

List<String> ls = newLinkedList<String>(); // 1

List<Object> lo = ls; // 2

lo.add(newObject()); // 3

String s = ls.get(0); // 4

Dieser Code wird vom Übersetzer so nicht akzeptiert!

  • Zeile 2 legt ein Alias der Object und String List an
  • Wir können ein Object in List<Object> einfügen (Zeile 3)
  • Aber wir fügen es gleichzeitig auch in lsein!
    • ls ist deklariert als List<String>…
    • Aber lswürde nun auch andere Objekttypen enthalten
  • Der Compiler erkennt das nicht, wenn Zeile 2 korrekt ist
  • Daher wird der Compiler Line 2 zurückweisen
    • “type mismatch: cannotconvertfromList<String>toList<Object>”
generics subtypen und unsere intuition
Generics, Subtypen und unsere Intuition
  • Sei Y eine Unterklasse von (Klasse oder Interface) X
  • Gsei eine generische Typdeklaration (z.B. List<E>)
  • G<Y>ist kein Subtyp von G<X>
  • Warum fällt uns das so schwer zu glauben?
  • Wir unterstellen, dass sich die Collection nicht ändert
    • Das ist natürlich oft eine falsche Annahme!
  • Angenommen, das Studierendensekretariat gibt eine Liste der Studenten an das Einwohnermeldeamt
    • Ein Student ist eine Person
    • Übergabe von List<Student> für List<Person>wirkt OK
    • Aber nur, wenn eine Kopie übergeben wird, keine Referenz!
    • Das Einwohnermeldeamt fügt eine Person (aber nicht Student) ein
      • Die gemeinsame (!) Studentenliste ist nun korrumpiert
generics subtypen und intuition
Generics, Subtypen und Intuition

Collection<Vehicle>

Collection<Car>

Punktweise

Subtypen,

sicher

Set<Vehicle>

Set<Car>

Kovariante Subtypen

nicht sicher

behandlung von subtypen wildcards
Behandlung von Subtypen: Wildcards
  • Wir wollen alle Elemente einer Collection ausgeben
  • Ohne Generics funktioniert das in etwa wie folgt:
  • Jetzt passen wir das eben schnell an Generics an:
  • Aber diese Methode akzeptiert nurCollection<Object>
    • Sie akzeptiert keineList<Person>, sondern nur List<Object>
    • Zur Erinnerung: List<Person> ist kein Subtypvon List<Object>

voidprintCollection(Collection c) {

for (Objectelement: c)

System.out.println(element);

}

void printCollection(Collection<Object> c) {

for (Object element: c)

System.out.println(element);

}

behandlung von subtypen wildcards1
Behandlung von Subtypen: Wildcards
  • Was müssen wir nun tun?
    • Wir brauchen einen gemeinsamen Obertypfür alle Collection-Arten
    • Das ist die “Collection von unbekannten Elementtypen”
    • In Java-Notation: Collection<?> (hier: List<?>)
  • Nun können wir den Code anpassen:
  • Die Elemente der Liste werden wie Objectbehandelt
    • Da java.lang.Object die Superklasse aller Typen ist
    • Daher passt jeder Typ – auch der „unbekannte Typ ?“ - zu Object

voidprintCollection(Collection<?> c) {

for (Objectelement: c)

System.out.println(element);

}

the unbekannte typ
The unbekannte Typ „<?>“
  • Der „Unbekannte Typ“ <?> wirkt sehr hilfreich
    • Wir können endlich auf die Elemente zugreifen!
    • Falls nötig, können wir sie auf einen konkreten Typ casten
      • Natürlich nur wenn der Cast gültig ist!
  • Wie steht es mit dem folgenden Code?
  • c hat den “unbekannten” Elementtyp “?”
  • Der einzufügende Typ muss zu “?” passen
    • Der Compiler kennt den tatsächlichen Typ von “?” nicht
    • Daher ist unklar, ob Object dazu passt
    • Man darf kein Object einfügen in eine List<String>!
    • Es kann nur der Wert null eingefügt werden

Collection<?> c = new ArrayList<String>();

c.add(newObject());

Dieser Code wird vom Übersetzer so nicht akzeptiert!

eine generische zeichenanwendung
Eine generische Zeichenanwendung
  • Wir betrachten folgendes Modell für ein Zeichentool:

abstractclassShape {

abstractvoiddraw(Canvas c);

}

class Circle extends Shape {

privateint x, y, radius;

voiddraw(Canvas c) { // ...

}

}

classRectangleextends Shape {

privateint x, y, width, height;

voiddraw(Canvas c) { // ...

}

}

classCanvas {

voiddraw(Shape s) {

s.draw(this);

}

}

eine generische zeichenanwendung1
Eine generische Zeichenanwendung
  • Jede Form erweitert Shape und kann sich zeichnen
  • Wir werden meist mehr als eine Form zeichnen!
  • Also speichern wir die Elemente in einer List<Shapes>
  • Wir passen den Code von Canvasentsprechend an:
  • Das funktioniert gut für jede List<Shape>
  • Und wenn wir eine List<Circle> zeichnen wollen?
    • Circle ist ein Subtyp von Shape
    • Aber List<Circle> ist kein Subtyp vonList<Shape>
    • Der Aufruf drawAll(List<Circle>) führt zu einem Compile-Fehler

voiddrawAll(List<Shape> shapes) {

for(Shape s: shapes) {

s.draw(this);

}

}

eine generische zeichenanwendung2
Eine generische Zeichenanwendung
  • drawAll passt an sich zu allen Shape-Subtypen
  • drawAll sollte mit List<Circle>, List<Shape>, List<Rectangle>, funktionieren…!
  • Der Einsatz von “?” bringt uns nicht weiter
    • Der unbekannte Typ ? hat keine Methode “draw(Canvas)”
    • Der Cast nach Shape ist möglich aber gefährlich
  • Wir müssen die Wildcard beschränken (“bound”)
    • Sie sollte alle Listen von Subtypen von Shape akzeptieren
  • In Java-Notation:

voiddrawAll(List<? extends Shape> shapes) {

for(Shape s: shapes) {

s.draw(this);

}

}

beschr nkte wildcards
Beschränkte Wildcards
  • Es gibt einen kleinen aber wichtigen Unterschied
    • List<Shape> akzeptiert nur genau List<Shape>
    • List<?extends Shape> akzeptiert Listen von allen Subtypen von Shape, inklusive Shape selbst
      • List<Shape>
      • List<Circle>
      • List<Rectangle>
  • “? extends X” bedeutet:
    • Wir kennen den exakten Type nicht ( “?”)
    • Aber wir wissen, dass der Typ zu X konform sein muss
    • X ist die „obere Schranke“ der Wildcard
beschr nkte wildcards1
Beschränkte Wildcards
  • Warum stimmt am folgenden Code nicht?
  • Wir können kein Rechteck in List<? extends Shape> einfügen
    • Wir kennen den exakten Typ der Elemente in shapes nicht
    • Wir wissen zwar, dass es ein Subtyp von Shape ist
    • Aber der Typ passt nicht unbedingt zu Rectangle
      • Der Aufruf könnte lauten addRectangle(List<Circle>);
  • Bei unbekanntem Typ können wir nur null einfügen
    • Einerseits schlecht…
    • Dafür gibt es keine Typprobleme zur Laufzeit!
    • Java geht hier den (typ-)sicheren Weg.

voidaddRectangle(List<? extends Shape> shapes) {

shapes.add(0, newRectangle());

}

Dieser Code wird vom Übersetzer so nicht akzeptiert!

schreiben von generischen methoden
Schreiben von generischen Methoden
  • Wie kopiert man alle Arrayelemente in eine Collection?
  • Wir können nicht Collection<Object> als Parameter nutzen
  • Aber Collection<?> wird auch nicht funktionieren!
    • Der Typ ist unbekannt, also passt jeder konkrete Typ potentiell nicht 

static void copyToCollection(Object[] array, Collection<?> c) {

for (Object o: array) {

c.add(o);

}

}

Dieser Code wird vom Übersetzer so nicht akzeptiert!

schreiben von generischen methoden1
Schreiben von generischen Methoden
  • Dazu schreiben wir eine generische Methode:
  • Das <T> besagt, dass dies eine generische Methode ist
    • T wird vor dem Rückgabewert eingeführt
  • T dient als formaler Parametertyp
  • Der Java Compiler nutzt das für Zugangskontrolle
    • Der Typ des Feldes und der Collection müssen konform sein
    • Es gilt der allgemeinste konforme Typ
  • Meist langt die Nutzung von Wildcards
    • Wildcards werden bevorzugt – klarer und knapper

static <T> voidcopyToCollection(T[]array, Collection<T> c) {

for (T o: array) {

c.add(o);

}

}

abfrage des typs
Abfrage des Typs
  • Abfrage des Basistyps eines generischen Typs:
  • Aber wir erhalten nicht den genauen generischen Typ
    • Es gibt nur eine Klasse List (siehe Folie 15)
    • Die compile-Zeit Information über den formalen Parameter ist zur Laufzeit nicht verfügbar
  • Wir können auch getClass() nutzen:
  • Die Ausgabe ist wieder true – getClass() liefert java.util.List

List<Circle> circles = new ArrayList<Circle>();

List<Rectangle> rects = new ArrayList<Rectangle>();

System.out.println(circlesinstanceof List); //  true

System.out.println(circlesinstanceof List<Circle>); // Fehler

System.out.println(circles.getClass() == rects.getClass());

untere schranken
Untere Schranken
  • Wir wollen eine Datensenke implementieren
    • Diese leert eine Collection und gibt das letzte Element zurück
  • Was ist hier das generische Typargument <T>?
    • Es gibt keinen gültigen Typ <T>, da die Typen nicht konform sind
    • Der Aufruf in der letzten Zeile ist damit illegal

interface Sink<T> {

voidflush(T t);

}

static<T> T flushAll(Collection<T> c, Sink <T> sink) {

T last = null;

for (T t: c) {

last = t;

sink.flush(last);

}

return last;

}

Sink<Object> sink;

Collection<String> collection;

String lastString = flushAll(collection, sink);

Dieser Code wird vom Übersetzer so nicht akzeptiert!

untere schranken1
Untere Schranken
  • Das scheint einfach zu beheben zu sein…:
  • Jetzt funktioniert der Aufruf!
  • Aber: T wird nun auf Object abgebildet
    • Weil das der Elementtyp des Sinks ist
    • Daher scheitert die Zuweisung des Ergebnisses an String
  • Wir benötigen “unbekannt, aber Oberklasse von T”
  • In Java-Notation: <? super T>

static <T> T flushAll(Collection<? extends T> c,

Sink<T> sink) {

static <T> T flushAll(Collection<T> c,

Sink<? super T> sink) {

implementierungsbeispiel
Implementierungsbeispiel

interface Sink<T> {

voidflush(T t);

}

classConcreteSink<T>implementsSink<T> {

publicvoidflush(T t) {

System.err.println("Flushing " + t + ", type: "

+t.getClass().getName());

}

}

static<T> T flushAll(Collection<T> c, Sink <? super T> sink) {

T last = null;

for(T t: c) {

last = t;

sink.flush(last);

}

returnlast;

}

Sink<Object> sink = newConcreteSink<Object>();

Collection<String> cs = Arrays.asList("a","bb2","cdf");

System.err.println(flushAll(cs, sink));

Bitte denken Sie sich den jeweiligen Klassenkontext dazu!

statische typisierung zusammenfassung
Statische Typisierung - Zusammenfassung
  • Statische Typsysteme sind Gegenstand sehr aktiver Forschung
  • Java-ähnliche Typsysteme sind begrenzt, aber im Allgemeinen können Typsysteme sehr mächtig und ausdrucksstark sein
    • Aber auch sehr kompliziert
  • Manche Programmierer sehen statische Typsysteme als eine Begrenzung ihrer Freiheit ("ich weiß, was ich tue")
  • Andere Programmierer denken, dass statische Typsysteme nicht nur viele Fehler erkennen, sondern auch eine gute Struktur im Code erzwingen ("erst denken, dann schreiben")
  • Die Diskussion ist noch nicht beendet.
  • Sie sollten eine fundierteMeinung darüber haben!
weiterf hrende literatur
Weiterführende Literatur
  • Die folgenden Materialien bieten (viel) mehr zu Generics:
    • Java Tutorial on Generics (byGiladBracha):

http://java.sun.com/docs/books/tutorial/extra/generics/

    • GiladBracha, “Generics in the Java Programming Language”
      • Diente als Hauptvorlage für diese Folien
      • http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
    • Maurice Naftalin, Philip Wadler: “Java GenericsandCollections”, O’Reilly, 2006