Classi astratte possono contenere sia metodi astratti che concreti (con implementazione), così come stati (campi di dati). Una classe può ereditare solo da una classe astratta. Le classi astratte vengono utilizzate quando è necessario astrarre una parte della logica e dello stato per una gerarchia di eredità.
Interfacce definiscono solo le firme dei metodi (fino a Java 8), a partire da Java 8 possono contenere metodi con implementazione predefinita (default) e metodi statici, ma non hanno stato. Una classe può implementare molte interfacce. Utilizza le interfacce per definire un insieme di comportamenti, indipendenti dalla gerarchia di eredità.
Esempio:
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("Runs"); } }
Qual è la differenza tra una classe astratta con solo metodi astratti e un'interfaccia prima di Java 8? Possono essere usati in modo intercambiabile?
Risposta: No, non sempre. Un'interfaccia non può contenere stato (campi di istanza) e supporta l'implementazione multipla, mentre l'ereditarietà della classe astratta è singola. Inoltre, le firme dei metodi dell'interfaccia sono sempre pubbliche.
Storia
Nel progetto era necessario implementare molte interfacce simili nel comportamento, ma appartenenti a gerarchie diverse. Lo sviluppatore ha scelto di utilizzare una classe astratta, il che ha reso impossibile l'uso dell'ereditarietà multipla. Questo ha portato a significativi rifattorizzazioni e duplicazione del codice.
Storia
In un grande progetto REST API è stata creata un'interfaccia con campi (costanti), supponendo che sarebbero state valori modificabili, come in una classe normale. Di conseguenza, il tentativo di modificare questi campi portava a un Silent Failure — i valori rimanevano invariati, e il bug non veniva trovato per lungo tempo.
Storia
Durante la migrazione a Java 8, gli sviluppatori hanno aggiunto metodi con implementazione all'interfaccia, ma hanno dimenticato di considerare che parte delle classi eredita anche metodi omonimi tramite una classe astratta. Questo ha portato a conflitti di metodi e risultati inaspettati durante la risoluzione dell'ereditarietà.