Lektion 20 - Vererbung
Klassen erweitern und Verhalten wiederverwenden
Vererbung bedeutet: Eine Klasse uebernimmt Eigenschaften und Methoden einer anderen Klasse. Dadurch kannst du Gemeinsamkeiten einmal definieren und spezielle Varianten darauf aufbauen.
1. Die Grundidee
Stell dir vor, du hast mehrere Arten von Benutzerkonten: normale User, Admins und Trainer. Alle haben einen Namen und koennen sich vorstellen. Admins haben aber zusaetzliche Rechte. Genau hier kann Vererbung helfen.
Gemeinsame Klasse: User
- name
- introduce()
Spezielle Klasse: AdminUser
- erbt name
- erbt introduce()
- bekommt zusaetzlich deleteContent()
Die allgemeine Klasse nennt man oft Basisklasse, Oberklasse oder Parent Class. Die spezielle Klasse nennt man Unterklasse oder Child Class.
2. Vererbung mit extends
In Dart nutzt du extends, wenn eine Klasse von einer anderen Klasse erben soll.
class User {
String name;
User(this.name);
void introduce() {
print('Hallo, ich bin $name.');
}
}
class AdminUser extends User {
AdminUser(String name) : super(name);
void deleteContent() {
print('$name loescht einen Beitrag.');
}
}
void main() {
final admin = AdminUser('Mina');
admin.introduce();
admin.deleteContent();
}
AdminUser besitzt die Methode introduce(), obwohl sie dort nicht
erneut geschrieben wurde. Sie kommt aus User.
3. Was macht super?
super spricht die Oberklasse an. Wenn die Oberklasse einen Konstruktor mit
Werten braucht, muss die Unterklasse diese Werte weitergeben.
class Animal {
String name;
Animal(this.name);
}
class Dog extends Animal {
Dog(String name) : super(name);
}
Der Hund hat selbst kein eigenes name-Attribut definiert. Er bekommt es durch
die Oberklasse Animal.
4. Methoden ueberschreiben mit @override
Manchmal soll eine Unterklasse eine geerbte Methode anders ausfuehren. Dann ueberschreibst du sie.
class Animal {
void makeSound() {
print('Ein Tier macht ein Geraeusch.');
}
}
class Dog extends Animal {
@override
void makeSound() {
print('Wuff!');
}
}
class Cat extends Animal {
@override
void makeSound() {
print('Miau!');
}
}
@override ist ein Hinweis fuer Dart und fuer andere Entwickler: Diese Methode
ersetzt bewusst eine Methode aus der Oberklasse.
5. Polymorphie: gleiche Form, anderes Verhalten
Polymorphie bedeutet: Du behandelst mehrere Objekte als denselben Obertyp, aber jedes Objekt reagiert passend zu seiner echten Klasse.
void main() {
final animals = <Animal>[
Dog(),
Cat(),
];
for (final animal in animals) {
animal.makeSound();
}
}
Die Liste kennt nur Animal. Trotzdem ruft Dart bei Dog die Hund-Methode
und bei Cat die Katzen-Methode auf.
6. Beispiel mit Lernsystem
Ein Coding-Lernsystem kann unterschiedliche Aufgabenarten haben. Alle Aufgaben haben einen Titel und Punkte. Manche Aufgaben sind Quiz-Aufgaben, andere Code-Aufgaben.
class LearningTask {
String title;
int points;
LearningTask(this.title, this.points);
void showInfo() {
print('$title bringt $points Punkte.');
}
}
class QuizTask extends LearningTask {
QuizTask(String title, int points) : super(title, points);
void startQuiz() {
print('Quiz startet: $title');
}
}
class CodeTask extends LearningTask {
CodeTask(String title, int points) : super(title, points);
void openEditor() {
print('Editor oeffnen fuer: $title');
}
}
Gemeinsamkeiten liegen in LearningTask. Spezielles Verhalten liegt in
QuizTask und CodeTask.
7. Vererbung vs. Komposition
Vererbung ist nicht immer die beste Loesung. Manchmal ist Komposition sauberer. Eine gute Lernfrage ist: Ist das Objekt wirklich eine spezielle Form des anderen Objekts?
Dog extends AnimalCar besitzt EngineFaustregel: Nutze Vererbung fuer "ist ein". Nutze Komposition fuer "hat ein".
8. Abstrakte Klassen
Eine abstrakte Klasse kann als Vorlage dienen. Von ihr sollen Objekte nicht direkt erstellt werden. Unterklassen muessen bestimmte Methoden umsetzen.
abstract class PaymentMethod {
void pay(double amount);
}
class PayPalPayment extends PaymentMethod {
@override
void pay(double amount) {
print('Bezahle $amount Euro mit PayPal.');
}
}
class CardPayment extends PaymentMethod {
@override
void pay(double amount) {
print('Bezahle $amount Euro mit Karte.');
}
}
PaymentMethod sagt: Jede Zahlungsart muss eine Methode pay haben.
Wie genau bezahlt wird, entscheidet die Unterklasse.
9. Vererbung in Flutter
Flutter nutzt Vererbung sehr stark. Wenn du ein eigenes Widget baust, erbst du
beispielsweise von StatelessWidget oder StatefulWidget.
class ProfileCard extends StatelessWidget {
const ProfileCard({super.key});
@override
Widget build(BuildContext context) {
return const Text('Profil');
}
}
ProfileCard ist ein spezielles StatelessWidget. Deshalb muss es die
Methode build bereitstellen.
10. Typische Fehler bei Vererbung
super vergessenDie Oberklasse bekommt benoetigte Konstruktorwerte nicht.@override hilft, Tippfehler zu erkennen.11. Vererbung planen
Bevor du Klassen schreibst, plane die gemeinsame Oberklasse und die Unterschiede der Unterklassen.
Erstelle Klasse Person
Attribut name
Methode introduce()
Erstelle Klasse Student erbt von Person
Attribut course
Ueberschreibe introduce()
Erstelle Klasse Teacher erbt von Person
Attribut subject
Ueberschreibe introduce()
Erzeuge Liste von Person
Fuege Student und Teacher hinzu
Rufe fuer jede Person introduce() auf
12. Aufgaben zur Vererbung
- Erstelle eine Klasse
Personmitnameund Methodeintroduce(). - Erstelle
Student, der vonPersonerbt und ein Attributcoursebesitzt. - Erstelle
Teacher, der vonPersonerbt und ein Attributsubjectbesitzt. - Ueberschreibe in beiden Unterklassen die Methode
introduce(). - Erzeuge eine
List<Person>und rufe fuer jedes Objektintroduce()auf.
class Person {
String name;
Person(this.name);
void introduce() {
print('Ich bin $name.');
}
}
class Student extends Person {
String course;
Student(String name, this.course) : super(name);
@override
void introduce() {
print('Ich bin $name und lerne $course.');
}
}
Zusatzaufgabe: Erstelle eine abstrakte Klasse Shape mit Methode
area(). Baue danach Circle und Rectangle als Unterklassen.