Innanzitutto quando vedi la parola "abstract" (su classi o metodi) devi pensare a qualcosa di "incompleto". Perché qualcosa dovrebbe poter non essere completo ... beh, questo è un discorso un po' più ampio che ti sarà più chiaro con una visione più estesa sulla programmazione ad oggetti.
La OOP consente due cose importanti: riutilizzo del codice ed estensione/specializzazione del codice altrui. Nei linguaggi non object-oriented (es. "C") è facilissimo riusare funzioni (anche fatte da altri) ma è estremamente difficile (se non impossibile) estendere e soprattutto "specializzare" codice altrui. In OOP invece è possibile e banale grazie al meccanismo della ereditarietà.
Ma nella OOP bisogna cercare quindi di pensare in termini di tipi che sono correlati tra di loro secondo una relazione di generalizzazione/specializzazione. Un Veicolo ha certe caratteristiche e certi comportamenti. Una Automobile la deriviamo da Veicolo, poiché saprà fare tutto ciò che Veicolo sa fare, magari con qualche operazione implementata diversamente ("specializzazione") o magari con qualcos'altro in più. Una Ferrari la deriviamo da Automobile perché anche qui farà tutto ciò che sa fare Automobile con qualcosa di meglio o in più. E così via.
La questione di classi astratte e interfacce è per fornire degli elementi di "astrazione", per rappresentare entità più generiche o tipicamente non concrete. Spesso faccio questo esempio basilare: una gerarchia di classi per rappresentare oggetti "solidi".
- Classe Solido
- Classe Cubo che estende Solido
- Classe Sfera che estende Solido
ecc...
Ha senso poter istanziare Solido? Solido che cosa? No, infatti non ha granché senso. Solido è un concetto generale, non qualcosa di reale, concreto che puoi misurare e toccare.
E in Java (come altri linguaggi ad oggetti) c'è un modo per far sì che Solido possa essere definito come qualcosa di non concreto, appunto una classe "astratta". Che fa sì che la classe NON possa essere istanziata direttamente ma che debba essere estesa in qualche modo più concreto per fare qualcosa di utile e reale.
Una interfaccia è da vedere concettualmente come una classe astratta al 100% (dimentichiamo per un attimo le novità di Java 8 sulle interfacce). La differenza tra classi astratte e interfacce sta nella ampiezza d'uso, dovuta anche e soprattutto al fatto che in Java una classe può estendere UNA sola classe ma può implementare N interfacce.
Le interfacce quindi si possono applicare a più classi anche "trasversalmente" in gerarchie differenti. E sovente una interfaccia serve per rappresentare la capacità di saper fare qualcosa.
Una interfaccia Pesabile che dichiara un double getPeso() potrebbe essere applicata sia alla gerarchia di Veicolo, sia alla gerarchia di Solido (con i dovuti dati necessari). Ma potrebbe anche essere applicata ad una gerarchia che ha alla base la classe Persona.