Autor: HM_JDSZielgruppe: Sek I

Uebungen und Vertiefung

Dieses Kapitel basiert auf dem umfangreichen OOP-Uebungsblatt. Es ist als Trainingssammlung angelegt und verbindet mehrere fortgeschrittene Themen in einem Paket:

  1. Kovarianz von Arrays
  2. Polymorphe Zuweisungen
  3. Casts und Laufzeitfehler
  4. statische Typisierung im Zusammenspiel mit Vererbung
  5. Regeln zu static, protected und Konstruktoren

Themenfokus des Arbeitsblatts

Das Blatt ist bewusst kein einzelnes Theoriepapier, sondern eine Serie von Pruef- und Begruendungsaufgaben. Ziel ist nicht nur "richtig/ falsch ankreuzen", sondern ein belastbares Modell davon, warum ein Ausdruck in Java erlaubt, verboten oder erst zur Laufzeit problematisch ist.

Array-Kovarianz in Java

Abbildung 1

Abbildung 2

Ein Schluesselbeispiel aus dem Blatt:

GeometrischeFigur[] figuren = new Kreis[10];
figuren[5] = new Rechteck(10, 20, 30);

Warum kompiliert das, obwohl es spaeter krachen kann?

  • Kreis[] ist in Java ein Subtyp von GeometrischeFigur[] (Kovarianz).
  • Der statische Typ der Referenz erlaubt also die Zuweisung.
  • Zur Laufzeit bleibt das echte Array aber ein Kreis[].
  • Ein Rechteck passt dort nicht hinein -> ArrayStoreException.

Das Beispiel zeigt sehr gut die Grenze zwischen Compile-Time-Typpruefung und Laufzeittyp.

Polymorphe Zuweisungen systematisch pruefen

Abbildung 1

Im Blatt werden Klassen wie Tier, Hund, Pudel, Katze verwendet. Die Kernregel:

  • Upcast (Subtyp -> Obertyp) ist ohne Cast erlaubt.
  • Downcast (Obertyp -> Subtyp) braucht Cast und kann zur Laufzeit fehlschlagen.

Beispiele:

Tier t1 = new Hund();      // erlaubt
Tier t2 = new Pudel();     // erlaubt
Hund h = new Pudel();      // erlaubt
Katze k = new Tier();      // nicht erlaubt ohne Cast (und fachlich unsicher)

Das Blatt trainiert genau diese Zuordnungspruefung in vielen Varianten.

Statischer Typ vs. dynamischer Typ

Merksatz (zentral fuer das ganze Arbeitsblatt):

  • Der statische Typ entscheidet, was der Compiler zulassen darf.
  • Der dynamische Typ entscheidet, was zur Laufzeit wirklich passiert.

Das gilt fuer:

  • Methodenaufrufaufloesung (Override)
  • gueltige Casts
  • Laufzeitfehler trotz erfolgreicher Kompilierung

Aufgabenklasse: "Welche Beziehungen muessen zwischen Klassen gelten?"

Das Arbeitsblatt nutzt auch abstrakte Variablenmengen wie O, X, T, M und gibt erlaubte/unerlaubte Zuweisungen vor. Daraus soll ein moegliches Klassendiagramm abgeleitet werden.

Warum ist das wertvoll?

  • Man denkt nicht nur in Syntax, sondern in Typgraphen.
  • Man trainiert Konsistenzbedingungen von Vererbung.
  • Man erkennt schneller, welche Zuweisungen strukturell unmoeglich sind.

Polymorphie plus statische Typisierung im Produktbeispiel

Abbildung 1

Abbildung 2

Abbildung 3

Abbildung 4

Abbildung 5

Abbildung 6

Im Blatt werden Typbeziehungen mit Klassen wie Produkt, LebensmittelProdukt, Steak, Haushaltsgeraet geprueft. Typische Fragen:

  1. Ist Produkt p = steak; erlaubt?
  2. Ist Haushaltsgeraet h = steak; erlaubt?
  3. Ist ein Cast moeglich, kompilierbar, aber zur Laufzeit unsicher?
  4. Wie verhaelt sich null in Typzuweisungen?

Besonders didaktisch stark ist dabei die Kombination aus:

  • direkten Zuweisungen
  • expliziten Casts
  • abstrakten Klassen (z. B. mit abstrakter Steuerberechnung)

Regeln fuer Casts

Kompilierbarkeit

Ein Cast kann kompilieren, wenn aus Sicht des Typbaums eine Umwandlung denkbar ist.

Laufzeitsicherheit

Ob der Cast wirklich funktioniert, haengt vom konkreten Objekt ab.

Produkt p = new Steak(...);
Steak s = (Steak) p; // sicher, wenn p wirklich auf Steak zeigt
Produkt p = new Haushaltsgeraet(...);
Steak s = (Steak) p; // kompiliert evtl., aber Laufzeitfehler

null als Sonderfall

Das Blatt enthaelt auch Aufgaben mit null.

  • null kann fast jedem Referenztyp zugewiesen werden.
  • Daraus folgt nicht, dass ein Methodenaufruf sicher ist.

Beispiel:

LebensmittelProdukt lmp = null;
Produkt p = lmp; // erlaubt
p.toString();    // NullPointerException

Kurzer Block zu static und Vererbung

Das Uebungsblatt prueft auch begriffliche Aussagen, z. B.:

  • Zugriff von nicht-statischen Methoden auf statische Variablen
  • Sichtbarkeit von protected
  • Konstruktoren und Vererbung

Wichtig fuer die Einordnung:

  • Konstruktoren werden nicht vererbt.
  • static-Member gehoeren zur Klasse, nicht zum Objekt.
  • protected sichert Erweiterbarkeit in Subklassen.

Loesestrategie fuer Zuweisungsaufgaben

  1. Vererbungsbaum skizzieren.
  2. Fuer jede Variable den statischen Typ notieren.
  3. Rechte Seite auf konkreten Objekttyp pruefen.
  4. Upcast/Downcast unterscheiden.
  5. Bei Casts separate Laufzeitpruefung notieren.

Diese Methode reduziert Fehler deutlich.

Typische Fehlannahmen

  1. "Wenn es kompiliert, ist es sicher" Falsch: Laufzeittypen koennen Casts oder Array-Zuweisungen brechen.
  2. "== prueft inhaltliche Gleichheit" Bei Objekten und Arrays vergleicht == Referenzen.
  3. "Ein Cast macht ungueltige Objekte gueltig" Ein Cast aendert nicht den realen Objekttyp.
  4. "null hat keinen Typ"null ist typkompatibel mit Referenztypen, bleibt aber nicht aufrufbar.

Vertiefungsaufgaben

  1. Baue eine kleine Tierhierarchie und pruefe 15 verschiedene Zuweisungen.
  2. Erstelle gezielt zwei Casts, die kompilieren, aber zur Laufzeit scheitern.
  3. Reproduziere eine ArrayStoreException mit einer kovarianten Arrayzuweisung.
  4. Erstelle dieselbe Aufgabe mit List<T> und vergleiche, warum Generics hier strenger sind.
  5. Formuliere fuer jede Aufgabe den statischen und dynamischen Typ explizit.

Zusammenfassung

Das OOP-Uebungsblatt trainiert den Uebergang von "Java schreiben" zu "Java begruenden":

  • Typbeziehungen verstehen statt nur auswendig lernen
  • Compile-Time- und Runtime-Sicht sauber trennen
  • Zuweisungen, Casts und Polymorphie mit System pruefen

Damit wird die Grundlage gelegt, um spaetere Framework- oder API-Codebasen sicher zu lesen und zu erweitern.

Systematik fuer Zuweisungsfragen

Das Arbeitsblatt enthaelt viele Aussagen der Form "Moeglich / Nicht moeglich". Eine zuverlaessige Loesestrategie besteht aus drei Ebenen:

  1. Kompiliert die Zuweisung?
  2. Ist ggf. ein Cast notwendig?
  3. Ist der Cast zur Laufzeit sicher?

Diese Trennung verhindert, dass Compile-Time- und Runtime-Regeln vermischt werden.

Typbaum als Pflichtwerkzeug

Vor jeder Beurteilung sollte ein Typbaum gezeichnet werden. Beispiel:

  • Tier
    • Hund
      • Pudel
    • Katze

Dann lassen sich Aussagen schnell pruefen:

  • Tier t = new Hund(); -> Upcast, erlaubt
  • Hund h = (Hund) t; -> Downcast, nur sicher wenn Objekt wirklich Hund/Pudel ist

Ohne diesen Typbaum entstehen in Aufgaben mit vielen Variablen schnell Denkfehler.

Compile-Time vs Runtime an Beispielen

Kompilierbar und sicher

Tier t = new Pudel();
Hund h = (Hund) t;

Kompilierbar, aber riskant

Tier t = new Katze();
Hund h = (Hund) t; // Laufzeitfehler

Nicht kompilierbar

Katze k = new Tier();

Das Blatt trainiert genau diese Unterschiede in mehreren Aufgabenvarianten.

Kovarianz bei Arrays kritisch einordnen

Die Array-Kovarianz ist historisch vorhanden, aber nicht voll typsicher. Das Blattbeispiel mit GeometrischeFigur[] und Kreis[] zeigt den klassischen Zielkonflikt:

  • Flexibilitaet bei Zuweisungen
  • moegliche ArrayStoreException zur Laufzeit

Didaktischer Vergleich:

  • Arrays sind kovariant.
  • Generics (List<T>) sind invariant und dadurch strenger.

Dieser Vergleich hilft, moderne Java-API-Entscheidungen besser zu verstehen.

null in Typaufgaben sauber behandeln

null ist bei Referenztypen meist zuweisbar. Das bedeutet aber nur Typkompatibilitaet, nicht Nutzbarkeit.

Beispiel:

LebensmittelProdukt lmp = null;
Produkt p = lmp; // erlaubt

Danach bleibt jeder Methodenaufruf auf p potentiell gefaehrlich, solange p == null nicht geprueft wurde.

Aufgaben zu static und Vererbung

Das Blatt kombiniert Polymorphie mit Aussagen zu Klassenvariablen und Methodenregeln. Wichtige Klarstellungen:

  1. static-Member gehoeren der Klasse.
  2. Nicht-statische Methoden koennen auf statische Member zugreifen.
  3. Konstruktoren werden nicht vererbt.
  4. protected dient Erweiterbarkeit in Subklassen.

Diese Aussagen sollten immer zusammen mit konkreten Codebeispielen geuebt werden.

Uebungsformat fuer Partnerarbeit

  1. Jede Gruppe bekommt 12 Zuweisungen.
  2. Fuer jede Zuweisung werden drei Spalten ausgefuellt:
    • kompiliert
    • Cast noetig
    • Laufzeitsicher
  3. Danach Gegencheck mit anderer Gruppe.

So wird aus Ankreuzwissen eine begruendete Typanalyse.

Laufzeitfehler reproduzierbar demonstrieren

Ein wichtiger Lernschritt ist bewusstes Reproduzieren von Fehlern:

  • ClassCastException
  • ArrayStoreException

Wenn Lernende diese Fehler einmal selbst provozieren und erklaeren, verankern sich die Regeln deutlich besser als durch reine Theorie.

Bewertungsraster fuer OOP-II-Uebungen

  1. Typbeziehungen korrekt dargestellt.
  2. Zuweisungen korrekt klassifiziert.
  3. Cast-Risiken erkannt.
  4. Runtime-Fehler plausibel begruendet.
  5. Begriffe (Upcast, Downcast, Kovarianz) korrekt verwendet.

Transfer in reale Codebasen

In Produktivcode tauchen dieselben Fragen auf, nur mit mehr Klassen:

  • API liefert Obertyp, konkrete Untertypen werden zur Laufzeit unterschieden.
  • Falsche Castannahmen fuehren zu sporadischen Laufzeitfehlern.
  • Unscharfe Typmodelle erschweren Wartung.

Das Blatt bereitet genau auf diese Situationen vor.

Abschluss der Vertiefung

Die Uebungsseite ist ein Kernkapitel fuer Typsicherheit in Java. Wer die dort trainierten Zuweisungs- und Castregeln wirklich beherrscht, kann polymorphen Code nicht nur schreiben, sondern auch korrekt analysieren und debuggen.

Abschlussserie zu Typbeziehungen

Zeichne fuer eine eigene Klassenhierarchie (mindestens 5 Klassen) einen Typbaum und pruefe 20 Zuweisungen.

Pro Zuweisung dokumentieren:

  1. kompiliert ja/nein
  2. Cast noetig ja/nein
  3. Laufzeitsicher ja/nein
  4. kurze Begruendung

Bonus:

  • Baue bewusst zwei Beispiele ein, die kompilieren, aber zur Laufzeit scheitern.

Diese Serie trainiert exakt die Kompetenz, die das Uebungsblatt in vielen Einzelaufgaben aufbaut.

Pruefungsnahe Kurzfragen

  1. Was unterscheidet statischen von dynamischem Typ?
  2. Wann ist ein Downcast syntaktisch erlaubt, aber gefaehrlich?
  3. Warum kann Array-Kovarianz zu Laufzeitfehlern fuehren?
  4. Welche Information benoetigst du, um die Sicherheit eines Casts zu beurteilen?

Diese Fragen verdichten die Kernideen der gesamten Uebungsseite.

Weiterfuehrende Praxisaufgabe

Erstelle ein eigenes Mini-Projekt mit einer Oberklasse und mindestens drei Subklassen. Formuliere 15 Zuweisungen und 8 Casts.

Fuer jeden Fall dokumentieren:

  1. statischer Typ links/rechts
  2. dynamischer Typ des Objekts
  3. kompiliert ja/nein
  4. Laufzeit sicher ja/nein

Ergaenze mindestens einen Fall mit ArrayStoreException und einen mit ClassCastException. Dadurch wird die Trennung von Typpruefung und Laufzeitverhalten nachhaltig gefestigt.

Mini-Reflexion

In OOP-II-Aufgaben entscheidet selten Intuition, sondern fast immer ein sauber gezeichneter Typbaum. Wer diese Visualisierung konsequent nutzt, trennt automatisch zwischen Kompilierbarkeit und Laufzeitverhalten.

Zusatzaufgabe

Waehle aus dem Uebungsblatt drei Beispiele mit Casts aus und erweitere sie jeweils um einen sicheren instanceof-Check. Begruende, wann dadurch nur ein Laufzeitfehler verhindert wird und wann auch die Lesbarkeit des Codes verbessert wird.

Schlussgedanke

Polymorphie ist erst dann wirklich verstanden, wenn Zuweisungen nicht geraten, sondern begruendet werden. Das Blatt trainiert genau diese Begruendungskompetenz.

Eine saubere Typanalyse ist kein Zusatz, sondern die Grundlage dafuer, polymorphen Code spaeter sicher zu erweitern, zu testen und in Reviews nachvollziehbar zu begruenden.