Kiste
EN

Klassen-Vervollständigung — `super` und Magic-Methods

Verfügbar — implementiert in Phase B.8. Vollständige Spec in docs/klassen-erweitert.md.

In Kapitel 22 (B.1) hast du die Klassen-Basis kennengelernt: klasse, Konstruktor neu, dies, Methoden, Vererbung mit erbt. B.8 schließt zwei Lücken: super für Eltern-Aufrufe und vier Magic-Methods, mit denen Klassen sich in eingebaute Operationen einklinken.

32.1 Vorher: Code-Duplikation in Kind-Konstruktoren

klasse Person {
    funktion neu(name, alter) {
        dies.name = name
        dies.alter = alter
    }
}

klasse Mitarbeiter erbt Person {
    funktion neu(name, alter, firma) {
        dies.name = name        // ← Duplikation
        dies.alter = alter      // ← Duplikation
        dies.firma = firma
    }
}

Wenn Person.neu Validierung oder Default-Werte hinzubekommt, muss Mitarbeiter.neu mitgepflegt werden. Bei drei Vererbungs-Ebenen dreimal.

32.2 Mit super: Eltern-Methode aufrufen

klasse Mitarbeiter erbt Person {
    funktion neu(name, alter, firma) {
        super.neu(name, alter)        // ruft Person.neu mit dem aktuellen dies
        dies.firma = firma
    }
    funktion vorstellen() {
        super.vorstellen()            // erst Eltern-Logik
        sag "  ↳ Firma: {dies.firma}"
    }
}

super ist ein Pseudo-Receiver, der die gleichnamige Methode der Eltern-Klasse mit demselben dies aufruft. Du kannst ihn nur innerhalb einer Methode verwenden, und nur wenn die Klasse mit erbt eine Eltern-Klasse hat. Sonst gibt's einen klaren Fehler.

32.3 Drei-Ebenen-Vererbung — super macht's stabil

klasse Tier {
    funktion ruf() { sag "Tier-Ruf" }
}
klasse Hund erbt Tier {
    funktion ruf() {
        super.ruf()
        sag "Hund-Ruf"
    }
}
klasse Welpe erbt Hund {
    funktion ruf() {
        super.ruf()
        sag "Welpe-Ruf"
    }
}

(neu Welpe()).ruf()
// Tier-Ruf
// Hund-Ruf
// Welpe-Ruf

Wichtig: super schaut von der Klasse aus, in der die laufende Methode definiert wurde — nicht von der Klasse, von der dies eine Instanz ist. Sonst würde Hund.ruf per super.ruf zu sich selbst springen, weil der Receiver eine Welpe-Instanz ist und Welpe's Eltern Hund ist. Diese Regel macht mehrstufige Vererbung sauber.

32.4 Magic-Method als_text — eigene Display-Form

Klassen rendern standardmäßig als <Klassenname>:

klasse Foo { }
sag (neu Foo())                        // <Foo>

Klammern nötig: sag neu Foo() direkt parst nicht — schreib sag (neu Foo()) oder erst nimm f = neu Foo() und dann sag f.

Mit einer als_text-Methode kannst du das überschreiben:

klasse Person {
    funktion neu(name, alter) {
        dies.name = name
        dies.alter = alter
    }
    funktion als_text() {
        gib "Person({dies.name}, {dies.alter})"
    }
}

nimm p = neu Person("Sascha", 38)
sag p                                  // Person(Sascha, 38)
sag als_text(p)                        // Person(Sascha, 38)
sag "Hallo {p}!"                       // Hallo Person(Sascha, 38)!

Die Methode wird automatisch von sag, als_text(...) und String-Interpolation gerufen. Rückgabe muss text sein — sonst Fehler.

32.5 Magic-Method gleicht — eigene Equality

Standardmäßig sind zwei Instanzen nur gleich, wenn sie dieselbe Reference sind:

klasse P { funktion neu(x) { dies.x = x } }
nimm a = neu P(5)
nimm b = neu P(5)
sag a == b                             // falsch (Pointer-Equality)

Mit gleicht definierst du Wert-Gleichheit:

klasse Person {
    funktion neu(name) { dies.name = name }
    funktion gleicht(andere) {
        gib typ(andere) == typ(dies) und dies.name == andere.name
    }
}

nimm a = neu Person("Anna")
nimm b = neu Person("Anna")
sag a == b                             // wahr
sag a != b                             // falsch

inst == nichts ist immer falsch — die gleicht-Methode wird nicht mit nichts gerufen, damit du in deinem Code nicht den nichts-Fall behandeln musst.

Klassen-Vergleich geht auf zwei Wegen, beide funktionieren ab B.8.5:

funktion gleicht(andere) {
    gib (typ(andere) == typ(dies)       // Workaround-Form, ohne Klassen-Namen
        und dies.name == andere.name)
}

// oder direkt mit dem Klassen-Namen:
funktion gleicht(andere) {
    gib (typ(andere) == Person           // direkt, lesbar (B.8.5+, E-089)
        und dies.name == andere.name)
}

Die typ(a) == typ(b)-Form ist nützlich in generischen Methoden, die ohne hartkodierten Klassen-Namen funktionieren sollen (z.B. eine Basis-Klasse, die in der Vererbung von Unterklassen genutzt wird). Die typ(a) == Klasse-Form ist lesbarer wenn die Klasse direkt beim Namen genannt wird. Beide produzieren denselben Wert.

32.6 Magic-Method vergleicht_mit — Sortier-Reihenfolge

Java-Style compareTo. Rückgabe < 0 / 0 / > 0:

klasse Wertung {
    funktion neu(p) { dies.p = p }
    funktion vergleicht_mit(andere) {
        gib dies.p - andere.p
    }
}

nimm a = neu Wertung(5)
nimm b = neu Wertung(10)

sag a < b                              // wahr
sag b > a                              // wahr
sag a <= a                             // wahr

Aktiviert <, <=, >, >=. Rückgabe muss ganz/komma/dezimal sein.

Bonus: wenn du vergleicht_mit definierst aber kein gleicht, fällt == automatisch auf vergleicht_mit(andere) == 0 zurück. Wer beides definiert: gleicht gewinnt für ==.

32.7 Magic-Method länge — eigene Length

klasse Sammlung {
    funktion neu(items) { dies.items = items }
    funktion länge() { gib länge(dies.items) }
}

nimm s = neu Sammlung([1, 2, 3, 4, 5])
sag länge(s)                           // 5

Rückgabe muss ganz ≥ 0 sein.

32.8 Was Magic-Methods NICHT sind

  • Keine speziellen Konstruktorenneu ist und bleibt der einzige Konstruktor-Name.
  • Keine versteckten Methoden — du kannst sie ganz normal aufrufen: inst.als_text() funktioniert.
  • Keine automatische Generierung — wer keine Magic-Method definiert, bekommt das Default-Verhalten.

32.9 Was kommt nicht in B.8

  • Sichtbarkeit öffentlich/privat — die Tokens sind reserviert, Semantik kommt später.
  • Statische Methoden (Foo.fabrik() ohne Instanz) — eigene Phase.
  • Operator-Overloading für +, -, * — eigene Phase falls je gewollt.
  • Subscript-Operator (inst[i]) — eigene Phase.
  • Iteration-Protokoll (wiederhole x in inst) — eigene Phase.

32.10 Praxis: 2D-Punkt mit allem drum-und-dran

klasse Punkt {
    funktion neu(x, y) {
        dies.x = x
        dies.y = y
    }
    funktion als_text() {
        gib "({dies.x}, {dies.y})"
    }
    funktion gleicht(andere) {
        gib (typ(andere) == typ(dies)
            und dies.x == andere.x
            und dies.y == andere.y)
    }
    funktion vergleicht_mit(andere) {
        // Sortiere primär nach x, sekundär nach y.
        wenn dies.x != andere.x {
            gib dies.x - andere.x
        }
        gib dies.y - andere.y
    }
    funktion verschoben(dx, dy) {
        gib neu Punkt(dies.x + dx, dies.y + dy)
    }
}

nimm a = neu Punkt(3, 7)
nimm b = a.verschoben(2, 0)
sag "a={a}  b={b}"                     // a=(3, 7)  b=(5, 7)
sag a < b                              // wahr (x=3 < x=5)
sag a == neu Punkt(3, 7)               // wahr