Klasy abstrakcyjne mogą zawierać zarówno metody abstrakcyjne, jak i konkretne (z implementacją), a także stany (pola danych). Klasa może dziedziczyć tylko z jednej klasy abstrakcyjnej. Klasy abstrakcyjne stosuje się, gdy konieczne jest uogólnienie pewnej części logiki i stanu dla hierarchii dziedziczących.
Interfejsy definiują tylko sygnatury metod (do Javy 8), począwszy od Javy 8 mogą zawierać metody z domyślną implementacją (default) i metody statyczne, ale nie mają stanu. Jedna klasa może implementować wiele interfejsów. Używaj interfejsów do definiowania zestawu zachowań, niezależnych od hierarchii dziedziczenia.
Przykład:
abstract class Animal { String name; public Animal(String name) { this.name = name; } abstract void makeSound(); } interface Movable { void move(); } class Dog extends Animal implements Movable { public Dog(String name) { super(name); } void makeSound() { System.out.println("Woof!"); } public void move() { System.out.println("Biegnie"); } }
Jaka jest różnica między klasą abstrakcyjną z wyłącznie abstrakcyjnymi metodami a interfejsem przed Javą 8? Czy można je używać zamiennie?
Odpowiedź: Nie, nie zawsze. Interfejs nie może zawierać stanu (pól instancji), a także wspiera wielokrotne implementacje, podczas gdy dziedziczenie klasy abstrakcyjnej jest pojedyncze. Ponadto, sygnatury metod interfejsu są zawsze publiczne.
Historia
W projekcie wymagano zrealizowania wielu interfejsów o podobnym zachowaniu, ale należących do różnych hierarchii obiektów. Programista zdecydował się na użycie klasy abstrakcyjnej, co uniemożliwiło zastosowanie wielokrotnego dziedziczenia. W przyszłości doprowadziło to do znacznego refaktoryzowania i duplikacji kodu.
Historia
W dużym projekcie REST API stworzono interfejs z polami (stałymi), zakładając, że będą to wartości zmienne, jak w zwykłej klasie. W rezultacie próby zmiany tych pól prowadziły do Silent Failure — wartości pozostawały takie same, błąd długo nie był znajdowany.
Historia
W procesie migracji na Javę 8 programiści dodali metody z implementacją do interfejsu, jednak zapomnieli uwzględnić, że część klas dziedziczy również metody o tej samej nazwie przez klasę abstrakcyjną. To doprowadziło do konfliktów metod i nieoczekiwanych wyników przy rozwiązywaniu dziedziczenia.