– Cześć, Amigo! Mam dla Ciebie nowy, fascynujący temat.

– Dzisiaj jest chyba dzień fascynujących tematów!

– Dziękuję!

– Proszę bardzo.

– Czy pamiętasz, kiedy wprowadziliśmy klasę bazową ChessItem, aby uprościć wszystkie klasy figur szachowych?

– Tak.

– To pomyśl sobie, że każda figura ma metodę, która zajmuje się wyświetleniem jej na ekranie. Wywołujesz ją, a wtedy figura rysuje się pod swoimi bieżącymi współrzędnymi. Czy nie byłoby lepiej przenieść tę metodę do klasy bazowej?

– Tak. Poznaliśmy już polimorfizm, moglibyśmy zatem wywołać metodę render dla wszystkich figur, niezależnie od ich typu. Wyglądałoby to mniej więcej tak:

Na przykład:
class ChessBoard
{
  public void drawAllChessItems()
  {
  //rysuje je niezależnie od ich typu.
  ArrayList <ChessItem> items = new ArrayList<ChessItem>();
  items.add(new King());
  items.add(new Queen());
  items.add(new Bishop());

  //rysuje je niezależnie od ich typu.
  for (ChessItem item: items)
  {
   item.draw();
  }
 }
}

– Dobra robota. Dokładnie tak. A co zrobiłaby metoda draw z samą klasą ChessItem?

– Nie wiem. Nie ma takiej figury w szachach. A to znaczy, że nie można jej przedstawić wizualnie.

– W rzeczy samej. Nie ma sensu tworzyć obiektu ChessItem. Taka figura w szachach nie istnieje. To tylko abstrakcja — klasa stworzona dla naszej wygody. Tak właśnie działa abstrakcja w OOP: przenosimy wszystkie ważne (wspólne dla wszystkich figur) dane i metody do klasy bazowej, a ich różnice przechowujemy w klasach odpowiadających konkretnym figurom szachowym.

Klasy abstrakcyjne -1

Java ma do tego specjalny typ: klasa abstrakcyjna. O klasach abstrakcyjnych musimy wiedzieć trzy ważne rzeczy.

1) Klasa abstrakcyjna może deklarować metody bez implementowania ich. Taka metoda nazywa się metodą abstrakcyjną.

Na przykład:
 public abstract class ChessItem
{
 public int x, y; //współrzędne
 private int value; //„wartość” danej figury

 public int getValue() //zwyczajna metoda, która zwraca wartość
 {
   return value;
 }

 public abstract void draw(); //metoda abstrakcyjna; brak implementacji

}

2) Metoda abstrakcyjna jest oznaczona słowem kluczowym abstract.

Jeśli klasa ma choćby jedną metodę abstrakcyjną, to jest ona także oznaczona słowem abstract.

3) Nie możesz tworzyć obiektów klasy abstrakcyjnej. Kod, który usiłuje to zrobić, po prostu się nie skompiluje.

Kod Java Opis
ChessItem item = new ChessItem();
item.draw();
Kod się nie kompiluje.
ChessItem item = new Queen();
item.draw();
Ale możesz zrobić coś innego.

4) Jeśli Twoja klasa dziedziczy klasę abstrakcyjną, musisz nadpisać wszystkie dziedziczone metody abstrakcyjne, tj. musisz je zaimplementować. Inaczej Twoja klasa będzie także musiała zostać zadeklarowana jako abstract. Jeśli klasa ma chociaż jedną niezaimplementowaną metodę, zadeklarowaną bezpośrednio w klasie bądź odziedziczoną po klasie macierzystej, to jest ona uznawana za abstrakcyjną.

– Czy na pewno jest to nam potrzebne? Do czego w ogóle służą klasy abstrakcyjne? Nie lepiej użyć zwyczajnych klas zamiast nich? Czy można zamiast metod abstrakcyjnych utworzyć puste implementacje, składające się z otwierającego i zamykającego nawiasu klamrowego?

– Można. Ale te ograniczenia są jak modyfikator private. Używamy modyfikatora private, aby celowo zablokować bezpośredni dostęp do danych, tak żeby inni programiści i ich klasy prawidłowo używali naszych metod oznaczonych jako public.

To samo dotyczy klasy abstrakcyjnej. Ten, kto napisał tę klasę, nie chce, żeby ktokolwiek tworzył jej instancje. Wprost przeciwnie – jej autor oczekuje, że metody abstrakcyjne jego klasy abstrakcyjnej będą dziedziczone bądź nadpisywane.

– Nadal nie rozumiem, po co aż tak komplikować sobie życie.

– Zalety tej funkcji są widoczne zwłaszcza w dużych projektach. Im więcej masz klas, tym bardziej potrzebujesz wyznaczyć ich role. Sam się o tym przekonasz i to niedługo. Każdy musi przez to przebrnąć.