Von Nova Trent, Junior Entwicklerin bei Java Fleet Systems Consulting

Schwierigkeit: 🟢 Einsteiger
Lesezeit: 35–40 Minuten
Voraussetzungen: Tag 1–6 abgeschlossen


📋 Kursübersicht: Java OOP in 10 Tagen

TagThemaStatus
1OOP-Konzepte & erste Klasse✅ Abgeschlossen
2Attribute & Methoden✅ Abgeschlossen
3Datenkapselung & Sichtbarkeit✅ Abgeschlossen
4Konstruktoren✅ Abgeschlossen
5Konstanten & Static✅ Abgeschlossen 🔴
6Vererbung – Grundlagen✅ Abgeschlossen
→ 7Polymorphie & Abstrakte Klassen📍 Du bist hier
8Typumwandlung & instanceof
9Interfaces & Enumerationen
10Ausnahmebehandlung🔴 KOPFNUSS

Voraussetzung: Tag 1–6 abgeschlossen


🔄 Kurz-Wiederholung: Challenge von Tag 6

Die Aufgabe war: Erstelle eine Motorrad-Klasse, die von Fahrzeug erbt.

Lösung:

public class Motorrad extends Fahrzeug {
    private int hubraum;
    private String typ;  // Chopper, Sportler, etc.
    
    public Motorrad(String marke, int baujahr, int hubraum, String typ) {
        super(marke, baujahr);  // Eltern-Konstruktor
        this.hubraum = hubraum;
        this.typ = typ;
    }
    
    @Override
    public void fahren(int km) {
        super.fahren(km);
        System.out.println("🏍️ Motorrad kurvt " + km + " km!");
    }
    
    public void wheelie() {
        System.out.println("🏍️ " + marke + " macht einen Wheelie!");
    }
    
    public int getHubraum() { return hubraum; }
    public String getTyp() { return typ; }
}

Hast du’s? Heute wird’s richtig spannend — Polymorphie!


⚡ Das Wichtigste in 30 Sekunden

Das Problem: Du hast verschiedene Fahrzeuge (Auto, Motorrad, LKW). Du willst sie alle gleich behandeln, aber jedes soll sich anders verhalten.

Die Lösung: Polymorphie! Eine Eltern-Variable kann verschiedene Kind-Objekte halten, und jedes verhält sich seiner Art entsprechend.

Heute lernst du:

  • ✅ Polymorphie — „Vielgestaltigkeit“
  • ✅ Methoden überschreiben mit @Override
  • ✅ Abstrakte Klassen — Templates für Kindklassen
  • ✅ Abstrakte Methoden — „Muss implementiert werden!“

Zeit-Investment: 35–40 Minuten


👋 Nova: „Das war mein Aha-Moment!“

Hey! 👋

Nova hier.

Real talk: Als ich Polymorphie verstanden habe, hat OOP endlich klick gemacht. Vorher dachte ich: „Warum so kompliziert? Ich kann doch einfach verschiedene Variablen nutzen.“

Aber stell dir vor: Du hast eine Liste mit 1000 Fahrzeugen — Autos, Motorräder, LKWs, alles durcheinander. Mit Polymorphie kannst du fahrzeug.fahren() aufrufen und jedes Fahrzeug macht automatisch das Richtige.

Das ist die Magie! 🚀


🟢 GRUNDLAGEN

Was ist Polymorphie?

„Polymorphie“ kommt aus dem Griechischen:

  • poly = viele
  • morph = Form/Gestalt

Eine Variable, viele Formen. Eine Fahrzeug-Variable kann ein Auto, ein Motorrad oder einen LKW halten — und jedes verhält sich anders.

Fahrzeug f;

f = new Auto("VW", 2024, 4);
f.fahren(10);  // "Auto fährt auf der Straße"

f = new Motorrad("Harley", 2023, 1200, "Chopper");
f.fahren(10);  // "Motorrad kurvt"

f = new LKW("MAN", 2022, 20000);
f.fahren(10);  // "LKW rollt schwer"

Abbildung 1: Eine Variable vom Eltern-Typ kann verschiedene Kind-Objekte halten.

Methoden überschreiben

Kindklassen können Methoden der Elternklasse überschreiben (override):

// Elternklasse
public class Fahrzeug {
    protected String marke;
    protected int kilometerstand;
    
    public void fahren(int km) {
        kilometerstand += km;
        System.out.println("Fahrzeug fährt " + km + " km.");
    }
}

// Kindklasse
public class Auto extends Fahrzeug {
    private int anzahlTueren;
    
    @Override  // Wichtig!
    public void fahren(int km) {
        kilometerstand += km;
        System.out.println("🚗 Auto fährt " + km + " km auf der Straße.");
    }
}

Die @Override-Annotation

@Override ist dein Sicherheitsnetz:

Abbildung 2: @Override schützt vor Tippfehlern — der Compiler prüft, ob du wirklich überschreibst.

// OHNE @Override — gefährlich!
public void faren(int km) {  // Tippfehler! Neue Methode statt Override!
    // ...
}

// MIT @Override — sicher!
@Override
public void faren(int km) {  // ❌ Compilerfehler: überschreibt nichts!
    // ...
}

Regel: IMMER @Override verwenden!

Polymorphie in Aktion

Der wahre Power-Move — eine Liste mit verschiedenen Objekten:

List<Fahrzeug> fuhrpark = new ArrayList<>();
fuhrpark.add(new Auto("VW Golf", 2024, 4));
fuhrpark.add(new Motorrad("Harley", 2023, 1200, "Chopper"));
fuhrpark.add(new Auto("BMW X5", 2022, 5));
fuhrpark.add(new LKW("MAN", 2021, 15000));

// Alle fahren — jeder auf seine Art!
for (Fahrzeug f : fuhrpark) {
    f.fahren(100);  // Polymorphie!
}

Output:

🚗 Auto fährt 100 km auf der Straße.
🏍️ Motorrad kurvt 100 km.
🚗 Auto fährt 100 km auf der Straße.
🚛 LKW rollt 100 km schwer.

super.methode() — Eltern-Methode aufrufen

Manchmal willst du die Eltern-Methode ERWEITERN, nicht ersetzen:

@Override
public void fahren(int km) {
    super.fahren(km);  // Erst Eltern-Code ausführen
    System.out.println("🚗 ...mit Klimaanlage!");  // Dann erweitern
}

🟢 Abstrakte Klassen

Das Problem

Was ist ein „Fahrzeug“ eigentlich? Kannst du ein „Fahrzeug“ fahren? Nein — du fährst ein Auto oder ein Motorrad.

Fahrzeug ist nur ein Konzept, keine echte Sache. Es sollte nicht instanziierbar sein!

Die Lösung: abstract

public abstract class Fahrzeug {
    protected String marke;
    protected int baujahr;
    
    public Fahrzeug(String marke, int baujahr) {
        this.marke = marke;
        this.baujahr = baujahr;
    }
    
    // Normale Methode — hat Implementierung
    public void info() {
        System.out.println(marke + " (" + baujahr + ")");
    }
    
    // Abstrakte Methode — KEINE Implementierung!
    public abstract void fahren(int km);
}

Abbildung 3: Abstrakte Klassen können nicht instanziiert werden — nur ihre Kindklassen.

Regeln für abstrakte Klassen

  1. Können nicht instanziiert werden: new Fahrzeug(...); // ❌ Compilerfehler! new Auto(...); // ✅ OK
  2. Können abstrakte Methoden haben: public abstract void fahren(int km); // Kein Body!
  3. Kindklassen MÜSSEN abstrakte Methoden implementieren: public class Auto extends Fahrzeug { @Override public void fahren(int km) { // MUSS implementiert werden! } }
  4. Können normale Methoden haben: public void info() { // Normale Methode mit Body System.out.println(marke); }

Abstrakte Methoden

Eine abstrakte Methode ist wie ein Vertrag: „Jede Kindklasse MUSS das implementieren!“

public abstract class Tier {
    protected String name;
    
    public Tier(String name) {
        this.name = name;
    }
    
    // Jedes Tier macht Geräusche — aber unterschiedlich!
    public abstract void lautGeben();
}

public class Hund extends Tier {
    public Hund(String name) {
        super(name);
    }
    
    @Override
    public void lautGeben() {
        System.out.println("🐕 " + name + ": Wuff!");
    }
}

public class Katze extends Tier {
    public Katze(String name) {
        super(name);
    }
    
    @Override
    public void lautGeben() {
        System.out.println("🐈 " + name + ": Miau!");
    }
}

Wann abstrakte Klasse?

SituationLösung
Gemeinsamer Code für KindklassenAbstrakte Klasse
Nur Methodensignaturen vorgebenInterface (Tag 9)
Instanziierung verhindernabstract
„Ist-ein“-BeziehungVererbung / abstrakte Klasse

🟡 PROFESSIONALS

Schon OOP-Erfahrung aus C#, C++, PHP? Hier sind die Java-Besonderheiten.

Für C#-Umsteiger

// C#
public abstract class Fahrzeug {
    public abstract void Fahren(int km);  // PascalCase
    public virtual void Info() { }        // virtual für Override
}
// Java
public abstract class Fahrzeug {
    public abstract void fahren(int km);  // camelCase
    public void info() { }                // Kein virtual nötig
}

In Java sind alle Methoden standardmäßig „virtual“ (überschreibbar). Nur final verhindert Override.

Für C++-Umsteiger

// C++
class Fahrzeug {
public:
    virtual void fahren(int km) = 0;  // Pure virtual = abstract
    virtual void info() { }            // Virtual für Override
};
// Java
public abstract class Fahrzeug {
    public abstract void fahren(int km);  // abstract = pure virtual
    public void info() { }                 // Implizit virtual
}

Für PHP-Umsteiger

// PHP
abstract class Fahrzeug {
    abstract public function fahren(int $km): void;
    public function info(): void { }
}
// Java
public abstract class Fahrzeug {
    public abstract void fahren(int km);
    public void info() { }
}

Fast identisch! PHP hat sich von Java inspirieren lassen.


🔵 BONUS

Für Wissbegierige: Template Method Pattern, sealed classes.

Template Method Pattern

Abstrakte Klassen sind perfekt für das Template Method Pattern:

public abstract class Bericht {
    // Template Method — definiert den Ablauf
    public final void generieren() {
        oeffnen();
        schreibeInhalt();  // Abstract — Kindklasse bestimmt
        schliessen();
    }
    
    private void oeffnen() {
        System.out.println("Bericht wird geöffnet...");
    }
    
    // Abstrakt — muss implementiert werden
    protected abstract void schreibeInhalt();
    
    private void schliessen() {
        System.out.println("Bericht wird geschlossen.");
    }
}

public class PDFBericht extends Bericht {
    @Override
    protected void schreibeInhalt() {
        System.out.println("Schreibe PDF-Inhalt...");
    }
}

public class HTMLBericht extends Bericht {
    @Override
    protected void schreibeInhalt() {
        System.out.println("Schreibe HTML-Inhalt...");
    }
}

Sealed Classes (Java 17+)

Mit sealed kannst du einschränken, welche Klassen erben dürfen:

public sealed class Fahrzeug permits Auto, Motorrad, LKW {
    // ...
}

public final class Auto extends Fahrzeug { }
public final class Motorrad extends Fahrzeug { }
public final class LKW extends Fahrzeug { }
// public class Fahrrad extends Fahrzeug { }  // ❌ Nicht erlaubt!

💬 Real Talk

Tom fragt, Nova erklärt — Die Fragen, die sich jeder stellt.

Java Fleet Kaffeeküche, 15:00 Uhr.


Tom: Nova, ich versteh nicht, warum ich @Override brauche. Die Methode wird doch trotzdem überschrieben?

Nova: Stimmt! Aber stell dir vor, du tippst dich. Ohne @Override erstellt Java einfach eine NEUE Methode. Mit @Override sagt der Compiler: „Hey, du wolltest überschreiben, aber die Methode gibt’s nicht!“

Tom: Okay, macht Sinn. Und abstrakte Klassen? Warum nicht einfach normale Klassen?

Nova: Weil du manchmal verhindern willst, dass jemand new Fahrzeug() macht. Was IST ein Fahrzeug? Es ist nur ein Konzept! Du kannst nur konkrete Dinge wie Auto oder Motorrad fahren.

Tom: Aber ich kann doch einfach den Konstruktor private machen?

Nova: Könntest du. Aber dann könnten Kindklassen nicht mehr super() aufrufen. Abstrakt ist sauberer und sagt klar: „Das ist ein Template, keine echte Klasse.“

Tom: Wann brauche ich Polymorphie in der Praxis?

Nova: Ständig! Denk an eine GUI: Du hast Buttons, Textfelder, Labels — alle erben von UIElement. Du kannst alle in einer Liste speichern und element.render() aufrufen. Jedes Element zeichnet sich selbst.


✅ Checkpoint: Hast du es verstanden?

Quiz

Frage 1: Was bedeutet „Polymorphie“?

Frage 2: Was passiert ohne @Override, wenn du einen Tippfehler im Methodennamen hast?

Frage 3: Kann eine abstrakte Klasse einen Konstruktor haben?

Frage 4: Dieser Code:

abstract class A {
    public abstract void foo();
    public abstract void bar();
}

class B extends A {
    @Override
    public void foo() { }
}

Kompiliert das? Warum (nicht)?

Frage 5: Was ist der Unterschied zwischen einer abstrakten Methode und einer normalen Methode?


Mini-Challenge

Aufgabe: Erstelle eine Mitarbeiter-Hierarchie:

  1. Abstrakte Klasse Mitarbeiter:
    • Attribute: name, personalnummer, gehalt
    • Normale Methode: arbeiten() — „Name arbeitet.“
    • Normale Methode: getJahresgehalt() — gehalt * 12
    • Abstrakte Methode: getRolle() — gibt Rolle als String zurück
  2. Kindklasse Entwickler:
    • Zusätzliches Attribut: programmiersprache
    • arbeiten() überschreiben: „Name programmiert in Sprache.“
    • getRolle() implementieren: „Entwickler“
  3. Kindklasse Manager:
    • Zusätzliches Attribut: teamgroesse
    • getJahresgehalt() überschreiben: +10% Bonus
    • getRolle() implementieren: „Manager“

Lösung: Findest du am Anfang von Tag 8! 🚀


❓ FAQ — Häufig gestellte Fragen

Frage 1: Kann eine abstrakte Klasse Attribute haben?

Ja! Abstrakte Klassen können alles haben, was normale Klassen haben — plus abstrakte Methoden.


Frage 2: Muss eine abstrakte Klasse abstrakte Methoden haben?

Nein! Eine Klasse kann abstract sein, nur um Instanziierung zu verhindern, ohne abstrakte Methoden.


Frage 3: Kann eine Kindklasse auch abstrakt sein?

Ja! Dann muss SIE die abstrakten Methoden nicht implementieren — aber ihre Kindklassen müssen es dann tun.


Frage 4: Was ist der Unterschied zu Interfaces?

Abstrakte Klassen können Attribute und Konstruktoren haben. Interfaces (Tag 9) sind „reiner“ — nur Methodensignaturen. Außerdem: Eine Klasse kann nur EINE Elternklasse haben, aber MEHRERE Interfaces implementieren.


Frage 5: Wann @Override, wann nicht?

IMMER @Override verwenden, wenn du überschreibst! Es gibt keinen Grund, es wegzulassen.


Frage 6: Kann ich super.methode() in einer abstrakten Methode aufrufen?

Nein — abstrakte Methoden haben keinen Body. Aber in überschriebenen NORMALEN Methoden kannst du super.methode() aufrufen.


Frage 7: Bernd meinte, Vererbung sei „überbewertet“. Was meint er?

seufzt Typisch Bernd. Er hat einen Punkt: Zu tiefe Vererbungshierarchien werden unübersichtlich. „Composition over Inheritance“ ist oft besser — statt Auto extends Fahrzeug extends Maschine extends Ding lieber separate Komponenten.

Aber für klare IS-A-Beziehungen (Auto IST EIN Fahrzeug) ist Vererbung perfekt. Wie immer: Balance! 🤷


📚 Quiz-Lösungen

Frage 1: Was bedeutet „Polymorphie“?

Antwort:

„Vielgestaltigkeit“ — eine Variable kann Objekte verschiedener Klassen halten, und jedes Objekt verhält sich seiner Klasse entsprechend.

Fahrzeug f = new Auto();  // f.fahren() → Auto-Verhalten
f = new Motorrad();       // f.fahren() → Motorrad-Verhalten

Frage 2: Was passiert ohne @Override bei Tippfehler?

Antwort:

Java erstellt eine neue Methode statt zu überschreiben! Der Code kompiliert, aber die Eltern-Methode wird aufgerufen statt deiner. Bug bleibt unentdeckt!


Frage 3: Kann abstrakte Klasse Konstruktor haben?

Antwort:

Ja! Der Konstruktor wird von Kindklassen mit super(...) aufgerufen. Die abstrakte Klasse selbst kann trotzdem nicht instanziiert werden.


Frage 4: Kompiliert das?

class B extends A {
    @Override
    public void foo() { }
}

Antwort:

Nein! B implementiert nur foo(), aber nicht bar(). Entweder bar() auch implementieren, oder B selbst abstract machen.


Frage 5: Abstrakte vs. normale Methode?

Antwort:

  • Normale Methode: Hat einen Body (Implementierung)
  • Abstrakte Methode: Hat KEINEN Body, nur Signatur. Kindklassen MÜSSEN sie implementieren.
public void normal() { /* Body */ }
public abstract void abstrakt();  // Kein Body!

🎉 Tag 7 geschafft!

Stark! 🚀

Das hast du heute gelernt:

  • ✅ Polymorphie — eine Variable, viele Formen
  • @Override — dein Sicherheitsnetz
  • ✅ Abstrakte Klassen — Templates für Kindklassen
  • ✅ Abstrakte Methoden — „Muss implementiert werden!“

Mehr als die Hälfte geschafft! 💪


🔮 Wie geht’s weiter?

Morgen in Tag 8: Typumwandlung & instanceof

Du lernst:

  • Upcast und Downcast
  • instanceof — Typ zur Laufzeit prüfen
  • Pattern Matching (Java 16+)
  • Sichere Typkonvertierung

Das wird praktisch! 🎯


📦 Downloads

Starter-Projekt für Tag 7:

⬇️ Tag07_Polymorphie.zip — Komplettes Maven-Projekt

Inhalt:

Tag07_Polymorphie/
├── pom.xml
├── README.md
└── src/main/java/
    └── de/javafleet/oop/
        ├── Main.java
        └── model/
            ├── Fahrzeug.java    (abstract)
            ├── Auto.java
            ├── Motorrad.java    (Lösung Tag 6)
            └── LKW.java

Grafiken:


🔧 Troubleshooting

Problem: „cannot instantiate abstract class“

error: Fahrzeug is abstract; cannot be instantiated

Lösung: Abstrakte Klassen können nicht mit new erstellt werden! Erstelle eine konkrete Kindklasse.


Problem: „must implement abstract method“

error: Auto is not abstract and does not override abstract method fahren()

Lösung: Deine Kindklasse muss ALLE abstrakten Methoden der Elternklasse implementieren!


Problem: „method does not override“

error: method does not override or implement a method from a supertype

Lösung: Tippfehler im Methodennamen oder falsche Signatur. Prüfe Elternklasse!


🔗 Resources & Links

🟢 Für Einsteiger

RessourceBeschreibungSprache
Oracle — PolymorphismOffizielle Doku🇬🇧
Oracle — Abstract ClassesOffizielle Doku🇬🇧

🟡 Für Fortgeschrittene

RessourceBeschreibungSprache
Effective Java — Item 20Abstract Classes vs Interfaces🇬🇧

👋 Bis morgen!

Tag 7 ist geschafft. Polymorphie und abstrakte Klassen sind jetzt dein Werkzeug!

Morgen: Typumwandlung & instanceof — damit du Objekte zur Laufzeit prüfen kannst.

See you! 🚀


Nova Trent
Junior Entwicklerin bei Java Fleet Systems Consulting
„Polymorphie ist, wenn du sagst ‚fahr!‘ und jedes Fahrzeug weiß, wie.“ 🎯


Praxis-Challenge

🎯 Zahlungssystem

Schwierigkeit: 🟢 Einsteiger
Geschätzte Zeit: 60–75 Minuten
Voraussetzungen: Tag 1–7 abgeschlossen


Szenario

Du entwickelst ein Zahlungssystem für einen Online-Shop. Kunden können mit verschiedenen Methoden bezahlen: Kreditkarte, PayPal, Überweisung oder Rechnung. Alle Zahlungsarten teilen gemeinsame Eigenschaften, haben aber unterschiedliche Verarbeitungslogik.

Das ist der perfekte Fall für abstrakte Klassen und Polymorphie!


Deine Aufgaben

1. Abstrakte Basisklasse Zahlung

Eine Zahlung kann nie „einfach so“ existieren — sie ist immer eine konkrete Zahlungsart. Daher: abstract!

ElementDetails
Attributebetrag (double), zeitstempel (LocalDateTime), status (String)
SichtbarkeitAttribute protected
KonstruktorParameter: betrag. Zeitstempel = LocalDateTime.now(), Status = "OFFEN"
Abstrakte Methodeboolean ausfuehren() — jede Zahlungsart implementiert das anders
Abstrakte MethodeString getZahlungsart() — gibt den Namen der Zahlungsart zurück
Konkrete Methodevoid stornieren() — setzt Status auf "STORNIERT", gibt Meldung aus
Konkrete MethodeString getQuittung() — gibt formatierte Quittung zurück
GetterFür alle Attribute

Hinweis zur Quittung:

--- QUITTUNG ---
Zahlungsart: [getZahlungsart()]
Betrag: [betrag] EUR
Status: [status]
Datum: [zeitstempel formatiert]
----------------

2. Konkrete Klasse Kreditkartenzahlung extends Zahlung

ElementDetails
Zusätzliche Attributekartennummer (String, letzte 4 Ziffern), karteninhaber (String)
KonstruktorParameter: betrag, kartennummer, karteninhaber
ausfuehren()Simuliert Zahlung: gibt true zurück wenn Betrag ≤ 5000, sonst false. Setzt Status entsprechend auf "BEZAHLT" oder "ABGELEHNT"
getZahlungsart()Gibt "Kreditkarte (****XXXX)" zurück (XXXX = letzte 4 Ziffern)

3. Konkrete Klasse PayPalZahlung extends Zahlung

ElementDetails
Zusätzliche Attributeemail (String)
KonstruktorParameter: betrag, email
ausfuehren()Prüft ob Email „@“ enthält. Wenn ja: Status = "BEZAHLT", return true. Sonst: Status = "FEHLGESCHLAGEN", return false
getZahlungsart()Gibt "PayPal (email)" zurück

4. Konkrete Klasse Ueberweisung extends Zahlung

ElementDetails
Zusätzliche Attributeiban (String), verwendungszweck (String)
KonstruktorParameter: betrag, iban, verwendungszweck
ausfuehren()Überweisungen sind immer „pending“: Status = "WARTEND", return true
getZahlungsart()Gibt "Überweisung (IBAN: XXXX...)" zurück (erste 4 Zeichen der IBAN)
Überschreibtstornieren() — ruft super.stornieren() auf, gibt zusätzlich „Rückbuchung wird eingeleitet…“ aus

5. Konkrete Klasse RechnungsKauf extends Zahlung

ElementDetails
Zusätzliche Attributerechnungsadresse (String), zahlungsziel (int, Tage)
KonstruktorParameter: betrag, rechnungsadresse, zahlungsziel
ausfuehren()Rechnungskauf nur bis 1000 EUR erlaubt. Wenn Betrag ≤ 1000: Status = "RECHNUNG_VERSENDET", return true. Sonst: Status = "ABGELEHNT", return false
getZahlungsart()Gibt "Rechnung (Zahlungsziel: XX Tage)" zurück
Neue MethodeLocalDateTime getFaelligkeit() — gibt zeitstempel.plusDays(zahlungsziel) zurück

6. Verwaltungsklasse ZahlungsManager

Diese Klasse zeigt Polymorphie in Aktion!

public class ZahlungsManager {
    private List<Zahlung> zahlungen = new ArrayList<>();
    
    // Zahlung hinzufügen und sofort ausführen
    public boolean verarbeite(Zahlung zahlung) { ... }
    
    // Alle Zahlungen eines bestimmten Status finden
    public List<Zahlung> findeNachStatus(String status) { ... }
    
    // Gesamtsumme aller erfolgreichen Zahlungen
    public double getGesamtUmsatz() { ... }
    
    // Alle Quittungen ausgeben
    public void druckeAlleQuittungen() { ... }
}

Hinweise zur Implementierung:

verarbeite(Zahlung zahlung):

  • Fügt die Zahlung zur Liste hinzu
  • Ruft zahlung.ausfuehren() auf (Polymorphie!)
  • Gibt das Ergebnis zurück

getGesamtUmsatz():

  • Summiert nur Zahlungen mit Status "BEZAHLT" oder "RECHNUNG_VERSENDET"

Test-Code für Main

public class Main {
    public static void main(String[] args) {
        
        ZahlungsManager manager = new ZahlungsManager();
        
        // Verschiedene Zahlungsarten erstellen
        Zahlung kreditkarte = new Kreditkartenzahlung(149.99, "1234", "Max Mustermann");
        Zahlung paypal = new PayPalZahlung(59.90, "max@example.com");
        Zahlung ueberweisung = new Ueberweisung(1250.00, "DE89370400440532013000", "Bestellung #12345");
        Zahlung rechnung = new RechnungsKauf(89.00, "Musterstr. 1, 12345 Berlin", 14);
        
        // Zu hohe Kreditkartenzahlung (wird abgelehnt)
        Zahlung zuHoch = new Kreditkartenzahlung(7500.00, "5678", "Erika Musterfrau");
        
        // Ungültige PayPal-Email
        Zahlung ungueltig = new PayPalZahlung(29.99, "keine-email");
        
        // Alle verarbeiten - Polymorphie in Aktion!
        System.out.println("=== Zahlungen verarbeiten ===");
        System.out.println("Kreditkarte: " + manager.verarbeite(kreditkarte));
        System.out.println("PayPal: " + manager.verarbeite(paypal));
        System.out.println("Überweisung: " + manager.verarbeite(ueberweisung));
        System.out.println("Rechnung: " + manager.verarbeite(rechnung));
        System.out.println("Zu hoch: " + manager.verarbeite(zuHoch));
        System.out.println("Ungültig: " + manager.verarbeite(ungueltig));
        
        // Gesamtumsatz
        System.out.println("\n=== Statistik ===");
        System.out.println("Gesamtumsatz: " + manager.getGesamtUmsatz() + " EUR");
        
        // Erfolgreiche Zahlungen
        System.out.println("\n=== Erfolgreiche Zahlungen ===");
        for (Zahlung z : manager.findeNachStatus("BEZAHLT")) {
            System.out.println("- " + z.getZahlungsart() + ": " + z.getBetrag() + " EUR");
        }
        
        // Eine Quittung ausgeben
        System.out.println("\n=== Beispiel-Quittung ===");
        System.out.println(kreditkarte.getQuittung());
        
        // Stornierung testen
        System.out.println("=== Stornierung ===");
        ueberweisung.stornieren();  // Soll zusätzliche Meldung ausgeben!
        System.out.println("Status nach Storno: " + ueberweisung.getStatus());
        
        // Fälligkeit bei Rechnung
        System.out.println("\n=== Rechnungsdetails ===");
        RechnungsKauf rk = (RechnungsKauf) rechnung;  // Cast nötig für spezielle Methode
        System.out.println("Fällig am: " + rk.getFaelligkeit());
    }
}

Erwartete Ausgabe (ungefähr)

=== Zahlungen verarbeiten ===
Kreditkarte: true
PayPal: true
Überweisung: true
Rechnung: true
Zu hoch: false
Ungültig: false

=== Statistik ===
Gesamtumsatz: 298.89 EUR

=== Erfolgreiche Zahlungen ===
- Kreditkarte (****1234): 149.99 EUR
- PayPal (max@example.com): 59.9 EUR

=== Beispiel-Quittung ===
--- QUITTUNG ---
Zahlungsart: Kreditkarte (****1234)
Betrag: 149.99 EUR
Status: BEZAHLT
Datum: 2025-01-06 14:30
----------------

=== Stornierung ===
Zahlung wurde storniert.
Rückbuchung wird eingeleitet...
Status nach Storno: STORNIERT

=== Rechnungsdetails ===
Fällig am: 2025-01-20T14:30:00

Checkliste: Das wird getestet

Abstrakte Klassen

  • [ ] Zahlung ist abstract deklariert
  • [ ] ausfuehren() und getZahlungsart() sind abstract
  • [ ] stornieren() und getQuittung() sind konkret implementiert
  • [ ] Man kann kein new Zahlung(...) erstellen

Polymorphie

  • [ ] List<Zahlung> enthält verschiedene konkrete Typen
  • [ ] zahlung.ausfuehren() ruft die richtige Implementierung auf
  • [ ] zahlung.getZahlungsart() gibt den korrekten Typ zurück

Methoden-Überschreibung

  • [ ] Alle konkreten Klassen implementieren die abstrakten Methoden
  • [ ] Ueberweisung.stornieren() erweitert mit super.stornieren()
  • [ ] @Override Annotation bei allen überschriebenen Methoden

Vererbung (Wiederholung Tag 6)

  • [ ] extends Zahlung bei allen konkreten Klassen
  • [ ] super(betrag) im Konstruktor
  • [ ] protected Attribute werden genutzt

Hinweise

  1. abstract Klassen können keine Instanzen habennew Zahlung(100) gibt Compilerfehler
  2. Abstrakte Methoden haben keinen Body — nur Signatur mit Semikolon
  3. Konkrete Klassen MÜSSEN alle abstrakten Methoden implementieren — sonst Compilerfehler
  4. Polymorphie: Die Variable ist vom Typ Zahlung, das Objekt ist z.B. Kreditkartenzahlung

Projektstruktur

src/main/java/
└── de/javafleet/oop/
    ├── Main.java
    ├── ZahlungsManager.java
    └── zahlung/
        ├── Zahlung.java
        ├── Kreditkartenzahlung.java
        ├── PayPalZahlung.java
        ├── Ueberweisung.java
        └── RechnungsKauf.java

Imports nicht vergessen!

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

Lösung: Findest du in Tag07_Polymorphie_Loesung.md


© 2025 Java Fleet Systems Consulting

Tags: #Java #OOP #Polymorphie #Abstract #Override #Tutorial

© 2025 Java Fleet Systems Consulting | java-developer.online

Autor

  • Ensign Nova Trent

    24 Jahre alt, frisch von der Universität als Junior Entwicklerin bei Java Fleet Systems Consulting. Nova ist brilliant in Algorithmen und Datenstrukturen, aber neu in der praktischen Java-Enterprise-Entwicklung. Sie brennt darauf, ihre ersten echten Projekte zu bauen und entdeckt dabei die Lücke zwischen Uni-Theorie und Entwickler-Realität. Sie liebt Star Treck das ist der Grund warum alle Sie Ensign Nova nennen und arbeitet daraufhin das sie Ihren ersten Knopf am Kragen bekommt.