Commit c178e38c by Patryk Czarnik

Dziedziczenie i polimorfizm

parent bce96f2b
package pcz.p12_dziedziczenie;
class Konto {
int numer;
int saldo;
Osoba wlasciciel;
Konto(int numer, int saldo, Osoba wlasciciel) {
this.numer = numer;
this.saldo = saldo;
this.wlasciciel = wlasciciel;
}
@Override
public String toString() {
return "Konto nr " + numer + ", saldo: " + saldo + ", wł.: " + wlasciciel;
}
void wplata(int kwota) {
saldo += kwota;
}
void wyplata(int kwota) {
saldo -= kwota;
}
void przelew(Konto kontoDocelowe, int kwota) {
this.saldo -= kwota;
kontoDocelowe.saldo += kwota;
}
}
package pcz.p12_dziedziczenie;
// Przy definicjach w tym pakiecie nie ma zadeklarowanego poziomu widoczności (public/private).
// W tej sytuacji obowiązuje widoczność domyślna=pakietowa.
class Osoba {
String imie, nazwisko;
int wiek;
static final int WIEK_PELNOLETNIOSCI = 18;
Osoba() {
}
Osoba(String imie, String nazwisko, int wiek) {
this.imie = imie;
this.nazwisko = nazwisko;
this.wiek = wiek;
}
// Ta metoda zostanie nadpisana w podklasach
void przedstawSie() {
System.out.println("Nazywam się " + imie + " " + nazwisko + " i mam " + wiek + " lat.");
}
// Ta metoda nie będzie nadpisywana w podklasach. Można by do niej dopisać final.
boolean pelnoletnia() {
return wiek >= WIEK_PELNOLETNIOSCI;
}
void postarzejSie() {
wiek += 1;
}
@Override
public String toString() {
return imie + " " + nazwisko + " (" + wiek + " lat)";
}
}
package pcz.p12_dziedziczenie;
public class Polimorfizm1 {
public static void main(String[] args) {
Osoba osoba = new Osoba("Ala", "Kowalska", 30);
System.out.println(osoba);
// Poprzez zmienną typu Osoba można korzystać tylko ze zmiennych (pól) i metod zdefiniowanych w klasie Osoba.
System.out.println(osoba.imie + " " + osoba.nazwisko);
osoba.przedstawSie();
// Nie działają:
// System.out.println(osoba.kierunek);
// System.out.println(osoba.sredniaOcen());
System.out.println();
Student student = new Student("Adam", "Abacki", 22, "geologia", 4);
System.out.println(student);
// Jeśli klasa Student jest rozszerzeniem klasy Osoba,
// to na zmiennej typu Student można wykonać te operacje (i dostać się do tych zmiennych),
// które byłyby dostępne zmiennej typu Osoba...
if(student.pelnoletnia()) {
System.out.println("Student " + student.imie + " jest pełnoletni");
}
// ... a dodatkowo dostępne są zmienne i metody dodane w klasie Student.
student.dodajOcene(5);
student.dodajOcene(4);
System.out.println("Średnia ocen: " + student.sredniaOcen());
System.out.println();
// Dziedziczenie to nie tylko "skopiowanie definicji z istniejącej klasy i dopisanie czegoś jeszcze".
// W dziedziczeniu chodzi też o to, że w każdej sytuacji obiekt podklasy może być użyty zamiast obiektu nadklasy.
// "zasada podstawiania"
// Skoro student posiada wszystkie te cechy, co inne osoby, to obiektu student można użyć w k ązdym miejscu, gdzie możnaby uzyć osoby.
// Przykłady:
// Student może być wpisany do zmiennej typu Osoba.
Osoba ktos = student;
// Osoba ktos = osoba;
ktos.przedstawSie();
// Student może być właścicielem konta:
Konto kontoStudenckie = new Konto(1313, 123, student);
kontoStudenckie.wplata(500);
System.out.println(kontoStudenckie);
System.out.println();
// Student może pójść do urzędu
Urzad urzadMiasta = new Urzad();
urzadMiasta.zalatwSprawe(student);
// itp....
// Czy w zmienne ktos mamy teraz Osobę czy Studenta?
System.out.println(ktos.getClass()); // Student
// Jednak do pól tego obiektu nie można dostać się "oficjalnie":
// System.out.println(ktos.kierunek + " , średnia " + ktos.sredniaOcen());
// Obiekt ze zmiennej typu "nadklasa" można zrzutować na typ "podklasa", jeśli widzimy, że w danym momencie jest to możliwe:
if(ktos instanceof Student) {
System.out.println("To jest Student");
Student zrzutowany = (Student)ktos;
System.out.println(zrzutowany.kierunek + " , średnia " + zrzutowany.sredniaOcen());
// albo tak jeśli czytamy tylko jedną rzecz:
System.out.println(((Student)ktos).rok);
} else {
System.out.println("To nie jest Student");
}
}
}
package pcz.p12_dziedziczenie;
public class Polimorfizm2 {
public static void main(String[] args) {
Osoba osoba = new Osoba("Ala", "Kowalska", 30);
Student student = new Student("Adam", "Abacki", 20, "prawo", 1);
System.out.println(osoba);
System.out.println(student);
System.out.println();
// 1) Podklasa może nie zmieniać metody z nadklasy - wtedy ta metoda jest "dziedziczona" i działa ta sama implementacja
System.out.println(student.pelnoletnia());
// 2) Podklasa może dodać zupełnie nowe metody
student.dodajOcene(3);
student.dodajOcene(4);
student.dodajOcene(5);
student.dodajOcene(5);
System.out.println(student.sredniaOcen());
// System.out.println(osoba.sredniaOcen());
System.out.println();
// 3) podklasa może też nadpisać (OVERRIDE) metodę, która istniała w nadklasie
// wtedy gdy metoda zostanie wywołana na obiekcie podklasy, zadziała jej zmieniona wersja
System.out.print(" osoba.przedstawSie(): ");
osoba.przedstawSie();
System.out.print("student.przedstawSie(): ");
student.przedstawSie();
System.out.println();
// Java użyje wersji metody z podklasy także wtedy, gdy obiekt podklasy (Student)
// będzie wpisany na zmienną typu nadklasa (Osoba):
Osoba x;
x = osoba;
x.przedstawSie();
x = student;
x.przedstawSie();
// bo w języku Java metody instancyjne są zawsze "wirtualne" (terminologia języka C++)
// Zauważmy, że x.przedstawSie() wypisuje informację o kierunku,
// ale sami bezpośrednio tej informacji nie odczytamy ze zmiennej x, bo jest ona typu Osoba
// System.out.println(x.kierunek);
System.out.println();
// Wpisywanie studenta do zmiennej Osoba x jest nienaturalne.
// Ale do podobnej sytuacji może dojść w zupełnie naturalny sposób.
// 1) Kolekcja może zawierać obiekty różnych klas:
Osoba[] osoby = {
new Osoba("Ala", "Kowalska", 30),
new Student("Adam", "Abacki", 20, "prawo", 1),
new Pracownik("Jan", "Kowalski", 50, "kierowca", 4321),
};
// Wtedy gdy przeglądamy zawartość w pętli element kolekcji widzimy jako obiekt nadklasy (Osoba)
for(Osoba o: osoby) {
o.przedstawSie();
}
System.out.println();
System.out.println("=========================");
System.out.println("Idziemy do urzędu...");
Urzad urzadMiasta = new Urzad();
// Do urzędu jako Osoba może pójść zwykła Osoba, może też pójść Student...
urzadMiasta.zalatwSprawe(osoba);
urzadMiasta.zalatwSprawe(student);
for(Osoba o: osoby) {
urzadMiasta.zalatwSprawe(o);
}
}
}
package pcz.p12_dziedziczenie;
public class Polimorfizm3 {
public static void main(String[] args) {
// Dzięki temu, że "Student jest Osobą" i "Pracownik jest Osobą",
// można obiekty tych klas umieści w tablicy (kolekcji itp.) osób.
Osoba[] osoby = {
new Osoba("Ala", "Kowalska", 44),
new Osoba("Ola", "Malinowska", 33),
new Student("Adam", "Abacki", 20, "medycyna", 1),
new StudentInformatyki("Karol", "Infobacki", 23, 3),
new Pracownik("Jan", "Kowalski", 40, "kierowca", 3300),
};
// Przeglądają elementy tablicy wiemy na pewno, że są to Osoby,
// ale dodatkowo może się okazać, że niektóre osoby są Student albo Pracownik
// Każda osoba posiada imię, nazwisko i wiek - można te dany odczytać.
// Każda osoba potrafi się przedstawić - można wywołać przedstawSie()
// ale w każdej podklasie ta metoda może mieć inną treść ("overriding").
// Wykona się wersja z właściwej klasy.
for(Osoba osoba: osoby) {
System.out.println("* kolejna osoba to " + osoba.imie + " " + osoba.nazwisko);
System.out.println(" " + osoba);
System.out.print(" ");
// System.out.println(osoba.kierunek); // nie każda osoba jest studentem
osoba.przedstawSie();
System.out.println(" to jest obiekt klasy: " + osoba.getClass().getSimpleName());
// Można jawnie sprawdzić czy zmienna wskazuje na obiekt określonej klasy
// (lub dalszej podklasy - np. StudentInformatyki byłby w tym sensie Studentem)
if(osoba instanceof Student) {
// Jeśli tak jest, to możemy bezpiecznie zrzutować (cast) zmienną na typ Student
Student student = (Student)osoba;
System.out.println(" To jest student kierunku " + student.kierunek
+ ", który ma średnią ocen " + student.sredniaOcen());
}
// natomiast taki if nie byłby prawdziwy dla obiektu klasy StudentInformatyki
if(osoba.getClass() == Student.class) {
System.out.println(" to jest konkretnie klasy Student");
}
System.out.println();
}
}
}
package pcz.p12_dziedziczenie;
import java.math.BigDecimal;
class Pracownik extends Osoba {
String zawod;
BigDecimal pensja;
Pracownik() {
}
Pracownik(String imie, String nazwisko, int wiek, String zawod, BigDecimal pensja) {
// wywołanie konstruktora z nadklasy
super(imie, nazwisko, wiek);
this.zawod = zawod;
this.pensja = pensja;
}
Pracownik(String imie, String nazwisko, int wiek, String zawod, double pensja) {
// wywołanie innego konstruktora z tej samej klasy
this(imie, nazwisko, wiek, zawod, BigDecimal.valueOf(pensja).setScale(2));
}
Pracownik(String imie, String nazwisko, int wiek, String zawod, int pensja) {
this(imie, nazwisko, wiek, zawod, BigDecimal.valueOf(pensja).setScale(2));
}
Pracownik(String imie, String nazwisko, int wiek, String zawod, String pensja) {
this(imie, nazwisko, wiek, zawod, new BigDecimal(pensja));
}
// tworzenie wielu konstruktorów w tej samej klasie to jest "przeciążanie konstruktorów" / "overloading"
@Override
public void przedstawSie() {
System.out.println("Dzień dobry, tu " + imie + ", pracuję jako " + zawod + " i zarabiam " + pensja + " zł.");
}
}
package pcz.p12_dziedziczenie;
import java.util.ArrayList;
import java.util.List;
// Klasa Student jest rozszerzeniem klasy Osoba.
// Klasa Student dziedziczy z klasy Osoba (inherits / inheritance).
// Klasa Osoba jest nadklasą (superclass), a klasa Student jest podklasą (subclass).
class Student extends Osoba {
// Obiekt klasy Student będzie posiadał wzystkie pola zdefiniowane w klasie Osoba oraz wszystkie pola zdefiniowane tutaj.
String kierunek;
int rok;
List<Integer> oceny = new ArrayList<>();
Student() {
// Tutaj kompilator wstawia automatycznie wywołanie konstruktora bezargumentowego z nadklasy
// super();
}
Student(String imie, String nazwisko, int wiek, String kierunek, int rok) {
// Jawne wywołanie konstruktora z nadklasy w celu przekazania do niego argumentów.
super(imie, nazwisko, wiek);
this.kierunek = kierunek;
this.rok = rok;
}
// Jeśli chodzi o metody, podklasa może:
// 1) nie zmieniać metod odziedziczonych z nadklasy
// przykłady: pelnoletnia()
// 2) dodać nowe metody, których nie było w nadklasie
void dodajOcene(int ocena) {
oceny.add(ocena);
}
double sredniaOcen() {
int suma = 0;
for(int ocena: oceny) {
suma += ocena;
}
return 1. * suma / oceny.size();
}
// 3) nadpisać (override) metody, które były zdefiniowane w nadklasie
// czyli dla istniejących metoda podać inną treść
// adnotacja @Override jest w takich sytuacjach zalecana, ale nieobowiązkowa - bez niej wszystko też działa
@Override
void przedstawSie() {
System.out.println(
"Hej, tu " + imie + " " + nazwisko + ", studiuję na " + rok + " roku kierunku " + kierunek + ".");
}
@Override
public String toString() {
return super.toString() + ", student " + rok + " roku kierunku " + kierunek;
}
}
package pcz.p12_dziedziczenie;
class StudentInformatyki extends Student {
// Czasami podklasa nie ma nowych pól ani metod, a jest wprowadzana po to, aby ustalić pewne szczegóły.
// Jest to podklasa / podzbiór w sensie matematycznym (logicznym).
public StudentInformatyki(String imie, String nazwisko, int wiek, int rok) {
// ustalamy, że kierunkiem studiów jest "informatyka" i nie może być inaczej
super(imie, nazwisko, wiek, "informatyka", rok);
}
}
package pcz.p12_dziedziczenie;
public class Urzad {
void zalatwSprawe(Osoba osoba) {
System.out.println("Proszę się przedstawić.");
osoba.przedstawSie();
if(osoba.pelnoletnia()) {
System.out.println("Obywatelu " + osoba.nazwisko + ", sprawa załatwiona, dokumenty do odbioru za 30 dni.");
} else {
System.out.println("Do załatwienia sprawy potrzebna jest zgoda rodzica.");
}
System.out.println();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment