Lekcja 13. Dziedziczenie i klasy pochodne
Trudny / egzaminacyjnyPo co się tego uczymy?
Dziedziczenie eliminuje powtarzanie tego samego kodu w wielu podobnych klasach — jedna z czterech głównych zasad OOP, obok hermetyzacji (Lekcja 11), polimorfizmu (Lekcja 14) i abstrakcji (Lekcja 15), i jeden z najczęściej sprawdzanych tematów na egzaminie INF.04.
Teoria
Wyobraź sobie, że masz klasy Programista i Kierownik — obie mają imię i pensję, ale każda dokłada coś swojego. Bez dziedziczenia musiałbyś PRZEPISAĆ pola Imie i Pensja w każdej z nich osobno. Dziedziczenie pozwala napisać wspólną część RAZ, w jednej klasie bazowej.
Klasa bazowa (nadklasa) to klasa, z której inne klasy DZIEDZICZĄ — zawiera wspólne pola i metody.
Klasa pochodna (podklasa) dziedziczy WSZYSTKO z klasy bazowej (poza prywatnymi polami) i może dodać własne, dodatkowe pola i metody.
Składnia dziedziczenia w C# — dwukropek po nazwie klasy pochodnej:
class Programista : Pracownik { public string JezykProgramowania; }
Klasa Programista automatycznie "ma" pola Imie i Pensja z klasy Pracownik, mimo że nie są zapisane bezpośrednio w Programista.
Słowo kluczowe base pozwala klasie pochodnej odwołać się do konstruktora lub metod klasy bazowej:
public Programista(string imie, double pensja, string jezyk) : base(imie, pensja) { JezykProgramowania = jezyk; }
To wywołuje najpierw konstruktor Pracownik (ustawiający Imie i Pensja), a potem dodaje własne pole JezykProgramowania.
Dziedziczenie modeluje relację "JEST RODZAJEM" — programista JEST RODZAJEM pracownika, kierownik JEST RODZAJEM pracownika. Jeśli relacja nie pasuje do "jest rodzajem", dziedziczenie prawdopodobnie nie jest właściwym narzędziem.
Schemat
KLASA BAZOWA
Pracownik
(Imie, Pensja, ObliczWyplate())
/
/
KLASA POCHODNA KLASA POCHODNA
Programista Kierownik
(+ JezykProgramowania) (+ ZespolPodwladnych)
dziedziczy WSZYSTKO dziedziczy WSZYSTKO
z Pracownik + dokłada z Pracownik + dokłada
własne pola/metody własne pola/metody
Przykład z życia
Wszystkie pojazdy silnikowe (samochód, motocykl, ciężarówka) mają wspólne cechy (silnik, koła, prędkość maksymalna) opisane raz w "pojeździe bazowym", a każdy konkretny rodzaj dokłada coś swojego — ciężarówka dokłada ładowność, motocykl liczbę kół.
Kod (C#)
using System;
class Pracownik
{
public string Imie;
public double Pensja;
public Pracownik(string imie, double pensja)
{
Imie = imie;
Pensja = pensja;
}
public void WyswietlInfo()
{
Console.WriteLine(Imie + " - pensja: " + Pensja + " zł");
}
}
class Programista : Pracownik // dziedziczy z Pracownik
{
public string JezykProgramowania;
public Programista(string imie, double pensja, string jezyk) : base(imie, pensja)
{
JezykProgramowania = jezyk;
}
}
class Program
{
static void Main()
{
Programista dev = new Programista("Kasia", 8000, "C#");
dev.WyswietlInfo(); // metoda ODZIEDZICZONA z klasy Pracownik
Console.WriteLine("Język: " + dev.JezykProgramowania); // pole WŁASNE klasy Programista
}
}
Komentarz i wyjaśnienie kodu
Zapis class Programista : Pracownik oznacza "Programista dziedziczy z Pracownik". Zwróć uwagę, że w Main() wywołujemy dev.WyswietlInfo(), mimo że ta metoda jest zdefiniowana TYLKO w klasie Pracownik — obiekt dev "otrzymał" ją automatycznie przez dziedziczenie. : base(imie, pensja) w konstruktorze Programista przekazuje te dwa parametry do konstruktora klasy bazowej Pracownik, żeby nie powtarzać przypisań Imie = imie; po raz drugi.
Ćwiczenie samodzielne
Zdefiniuj klasę bazową Zwierze z polem Imie i metodą WydajDzwiek(), oraz klasę pochodną Pies dodającą pole Rasa.
Plac zabaw — wypróbuj online
Poniższy kod wykonuje się od razu w Twojej przeglądarce — nic nie trzeba instalować. Zmień kod i kliknij „Uruchom”.
Zadania do pracy własnej
Utwórz klasę bazową
Pojazd(pola:Marka,PredkoscMax) i klasę pochodnąMotocykldodającą poleLiczbaKol(zwykle 2).Rozbuduj przykład z lekcji o drugą klasę pochodną
Kierownik : Pracownik, dodającą poleZespolPodwladnych(liczba osób w zespole) i metodęWyswietlZespol().Zbuduj hierarchię: klasa bazowa
KontoBankowe(Lekcja 11, pola:Saldo, metody:Wplac/Wyplac) i dwie klasy pochodne —KontoOszczednosciowe(dodaje poleOprocentowanieRocznei metodęNaliczOdsetki()) orazKontoFirmowe(dodaje poleNazwaFirmyi limit debetu). Utwórz po jednym obiekcie każdego typu i przetestuj wszystkie metody.
Typowe błędy
Częsty błąd to zapomnienie wywołania konstruktora bazowego przez : base(...), gdy klasa bazowa NIE MA konstruktora bezparametrowego — kompilator zgłosi błąd, bo nie wie, jak zainicjować odziedziczone pola. Drugi błąd: dziedziczenie "na siłę" tam, gdzie relacja nie jest logicznie "jest rodzajem" (np. dziedziczenie Silnik po Samochod, mimo że silnik nie JEST rodzajem samochodu, tylko jego CZĘŚCIĄ — to relacja kompozycji, inny temat). Trzeci: próba dostępu z klasy pochodnej do PRYWATNYCH pól klasy bazowej — dziedziczenie nie omija hermetyzacji, trzeba użyć protected zamiast private, jeśli klasa pochodna ma mieć bezpośredni dostęp.
Nawiązanie do egzaminu zawodowego
To bezpośrednia realizacja efektu INF.04.4.7 z podstawy programowej: "definiuje klasy pochodne" — konkretnie kryteria dotyczące hierarchii dziedziczenia, klas bazowych i pochodnych.