archetype | title | linkTitle | author | readings | tldr | outcomes | quizzes | youtube | fhmedia | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
lecture-cg |
Generics und Polymorphie |
Polymorphie |
Carsten Gips (HSBI) |
|
Auch mit generischen Klassen stehen die Mechanismen Vererbung und Überladen zur Verfügung. Dabei muss
aber beachtet werden, dass generische Klassen sich **"invariant"** verhalten: Der Typ selbst folgt der
Vererbungsbeziehung, eine Vererbung des Typ-Parameters begründet _keine_ Vererbungsbeziehung! D.h.
aus `U extends O` folgt **nicht** `A<U> extends A<O>`.
Bei Arrays ist es genau anders herum: Wenn `U extends O` dann gilt auch `U[] extends O[]` ... (Dies
nennt man "_kovariantes_" Verhalten.)
|
|
|
|
::: cbox
B<E> extends A<E>
:::
\bigskip \bigskip
::: notes
class A<E> { ... }
class B<E> extends A<E> { ... }
A<Double> ad = new B<Double>();
A<String> as = new B<String>();
:::
class Vector<E> { ... }
class Stack<E> extends Vector<E> { ... }
Vector<Double> vd = new Stack<Double>();
Vector<String> vs = new Stack<String>();
\bigskip => Polymorphie bei Generics bezieht sich auf Typ (nicht Typ-Parameter)
::: notes
Invarianz: Generics sind invariant, d.h. ein HashSet<String>
ist ein
Untertyp von Set<String>
. Bei der Vererbung muss der Typ-Parameter identisch
sein.
:::
\bigskip
::: cbox
"B extends A
" [bedeutet nicht]{.alert} "C<B> extends C<A>
"
:::
\bigskip \bigskip
Stack<Number> s = new Stack<Integer>(); // DAS GEHT SO NICHT!
// Folgen (wenn obiges gehen wuerde):
s.push(new Integer(3)); // das ginge sowieso ...
// Folgen (wenn obiges gehen wuerde):
// Stack<Number> waere Oberklasse auch von Stack<Double>
s.push(new Double(2.0)); // waere dann auch erlaubt ...
// Das Objekt (Stack<Integer>) kann aber keine Double speichern!
// Zur Laufzeit keine Typ-Informationen mehr!
::: notes
- Typ-Löschung => zur Laufzeit keine Typinformationen vorhanden
- Compiler muss Typen prüfen (können)! :::
::: cbox
Wenn "B extends A
" dann "B[] extends A[]
"
:::
\bigskip \bigskip
Object[] x = new String[] {"Hello", "World", ":-)"};
x[0] = "Hallo";
x[0] = new Double(2.0); // Laufzeitfehler
String[] y = x; // String[] ist KEIN Object[]!!!
\bigskip
- Arrays besitzen Typinformationen über gespeicherte Elemente
- Prüfung auf Typ-Kompatibilität zur Laufzeit (nicht Kompilierzeit!)
[Hinweis auf Java-Geschichte [(Java-Insel: "Type Erasure")]{.notes}]{.bsp href="https://openbook.rheinwerk-verlag.de/javainsel/11_002.html#u11.2.2"}
::: notes Arrays gab es sehr früh, Generics erst relativ spät (ab Java6) => bei Arrays fand man das Verhalten natürlich und pragmatisch (trotz der Laufzeit-Überprüfung).
Bei der Einführung von Generics musste man Kompatibilität sicherstellen (alter Code soll auch mit neuen Compilern übersetzt werden können - obwohl im alten Code Raw-Types verwendet werden). Außerdem wollte man von Laufzeit-Prüfung hin zu Compiler-Prüfung. Da würde das von Arrays bekannte Verhalten Probleme machen ...
Kovarianz: Arrays sind kovariant, d.h. ein Array vom Typ String[]
ist wegen
String extends Object
ein Untertyp von Object[]
.
[Beispiel arrays.X]{.bsp href="https://github.com/Programmiermethoden-CampusMinden/PM-Lecture/blob/master/markdown/generics/src/arrays/X.java"} :::
=> Keine Arrays mit parametrisierten Klassen!
\bigskip \bigskip
Foo<String>[] x = new Foo<String>[2]; // Compilerfehler
Foo<String[]> y = new Foo<String[]>(); // OK :)
::: notes Arrays mit parametrisierten Klassen sind nicht erlaubt! Arrays brauchen zur Laufzeit Typinformationen, die aber durch die Typ-Löschung entfernt werden. :::
Vererbung:
- IS-A-Beziehung
- Anwendung: Vererbungsbeziehung vorliegend, Eigenschaften verfeinern
- Beispiel: Ein Student ist eine Person
\bigskip
Generics:
- Schablone (Template) für viele Datentypen
- Anwendung: Identischer Code für unterschiedliche Typen
- Beispiel: Datenstrukturen, Algorithmen generisch realisieren
- Generics: Vererbung und Überladen möglich, aber: \newline
Aus "
U extends O
" folgt [nicht]{.alert} "A<U> extends A<O>
"
\smallskip
- Achtung: Bei Arrays gilt aber: Wenn "
U extends O
" dann gilt auch "U[] extends O[]
" ...
::: slides
Unless otherwise noted, this work is licensed under CC BY-SA 4.0. :::