Lekcje modułu (2/2)
Polimorfismo i Klasy Abstrakcyjne
Polimorfizm i klasy abstrakcyjne to zaawansowane pojęcia programowania obiektowego, które pozwalają na pisanie elastycznego, rozszerzalnego kodu niezależnego od konkretnych implementacji.
Polimorfizm
Termin polimorfizm (wielopostaciowość) odnosi się do możliwości traktowania obiektów różnych klas tak, jakby należały do wspólnego typu. W Javie zmienna typu klasy nadrzędnej może przechowywać referencję do obiektu dowolnej klasy podrzędnej.
class Animal {
public void makeSound() {
System.out.println("Verso...");
}
}
class Dog extends Animal {
public void makeSound() {
System.out.println("Woof");
}
}
class Cat extends Animal {
public void makeSound() {
System.out.println("Miao");
}
}
Dzięki polimorfizmowi możemy zrobić tak:
Animal myAnimal1 = new Dog(); // Polimorfismo
Animal myAnimal2 = new Cat(); // Polimorfismo
myAnimal1.makeSound(); // Esegue il metodo di Dog (Woof)
myAnimal2.makeSound(); // Esegue il metodo di Cat (Miao)
Decyzja o tym, która metoda ma zostać wywołana, zapada w czasie wykonywania programu (późne wiązanie / Late Binding lub dynamic dispatch) na podstawie rzeczywistego obiektu, a nie typu zmiennej referencyjnej.
Klasy abstrakcyjne (abstract)
Klasa abstrakcyjna to klasa oznaczona słowem kluczowym abstract, której nie można bezpośrednio utworzyć (nie można wywołać new MyAbstractClass()). Służy jako "częściowy szablon" dla innych klas.
Metody abstrakcyjne
Klasa abstrakcyjna może zawierać metody abstrakcyjne: metody zadeklarowane bez ciała (bez nawiasów klamrowych i bez kodu), zakończone średnikiem. Nieabstrakcyjne klasy podrzędne są zobowiązane do zaimplementowania wszystkich odziedziczonych metod abstrakcyjnych.
abstract class Shape {
String color;
// Metodo astratto (senza corpo)
public abstract double getArea();
}
class Circle extends Shape {
double radius;
public Circle(double radius) {
this.radius = radius;
}
// Obbligatorio implementare getArea
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
Dynamic Binding / Late Binding
Skąd Java wie, którą metodę wykonać w czasie wykonywania programu? Proces ten jest znany jako późne wiązanie (Late Binding lub Dynamic Binding). W przeciwieństwie do przeciążania (rozstrzyganego w czasie kompilacji), kompilator nie wie, która konkretna metoda zostanie wykonana. Zamiast tego generuje ogólną instrukcję wywołania. W czasie wykonywania wirtualna maszyna Javy (JVM) bada rzeczywisty obiekt w pamięci i wywołuje implementację metody zdefiniowaną w klasie tego obiektu.
Klasy abstrakcyjne i brak implementacji
Jeśli klasa podrzędna dziedziczy po klasie abstrakcyjnej, ma dwie opcje:
- Zaimplementować wszystkie metody abstrakcyjne: w tym przypadku klasa może być zwykłą klasą (konkretną) i można tworzyć jej instancje.
- Zadeklarować się jako abstrakcyjna: jeśli klasa podrzędna nie zapewnia implementacji wszystkich odziedziczonych metod abstrakcyjnych, musi sama zostać zadeklarowana ze słowem kluczowym
abstract.
abstract class Animal {
public abstract void makeSound();
}
// Questa classe DEVE essere astratta perché non implementa makeSound()
abstract class Canine extends Animal {
// Eredita makeSound() ma non lo implementa
}
Spróbuj sam
Zadeklaruj zmienną s typu Shape i przypisz jej nowy obiekt Circle za pomocą polimorfizmu.
Pokaż wskazówkę
Wpisz `Shape s = new Circle();`, aby użyć typu bazowego jako typu referencji.
Rozwiązanie dostępne po 3 próbach
Uczyń klasę Shape abstrakcyjną i dodaj metodę abstrakcyjną double getArea(). Następnie uzupełnij klasę Square tak, aby rozszerzała Shape i implementowała getArea() zwracając side * side.
Pokaż wskazówkę
Zadeklaruj `public abstract double getArea();` w Shape. W Square dodaj `@Override public double getArea() { return side * side; }`.
Rozwiązanie dostępne po 3 próbach
Zadeklaruj w metodzie main tablicę typu Shape[] zawierającą obiekt Circle o promieniu 2.0 i obiekt Square o boku 3.0. Następnie użyj pętli (for lub for-each), aby wypisać w konsoli pole powierzchni każdej figury, wywołując metodę getArea().
Pokaż wskazówkę
Zadeklaruj tablicę za pomocą `Shape[] shapes = { new Circle(2.0), new Square(3.0) };` i przejdź po niej pętlą `for (Shape s : shapes)` wypisując `s.getArea()`.
Rozwiązanie dostępne po 3 próbach