Quando utilizzare: metodo predefinito dell'interfaccia Java 8+, rispetto al metodo astratto


540

Java 8 consente l'implementazione predefinita dei metodi nelle interfacce chiamate Metodi predefiniti .

Sono confuso tra quando dovrei usare quel tipo di interface default method, anziché un abstract class(con abstract method(s)).

Quindi quando utilizzare l'interfaccia con i metodi predefiniti e quando utilizzare una classe astratta (con i metodi astratti)? Le classi astratte sono ancora utili in quello scenario?


38
Forse non puoi ancora avere campi, metodi privati, ecc. Nelle interfacce, mentre puoi in classe astratta?
sp00m,

2
Mi stavo chiedendo prima su questo argomento, ora sono chiaro. Grazie a @Narendra Pathai. Vorrei aggiungere un link di un altro thread che ti è stato chiesto in merito allo stesso argomento, poiché entrambi questi erano i miei dubbi. stackoverflow.com/questions/19998309/…
Ashutosh Ranjan,


A volte puoi ancora codificare una classe base come interfaccia anche se la classe base ha stato. È solo che l'interfaccia deve definire setter e getter per lo stato e le classi concrete devono implementarli e definire il campo. Una limitazione su questo è che in una classe astratta, la proprietà bean può essere privata o protetta. Nelle interfacce hanno solo metodi pubblici. Quindi uno dei motivi per cui useresti una classe base astratta è se le tue classi hanno una proprietà che deve essere privata o protetta.
DaBlick,

@DaBlick Impossibile risolvere il problema di stato in un'interfaccia tramite HashMap. Es: se vuoi una classe Foo che contiene int a, b, String c. e vuoi che abbiano lo stato, crea un HashMap </ * nome dell'oggetto Foo * / String, / * map of fields * / Hashmap </ * name specific Field * / String, / * field field * / Object >> map . Quando vuoi "istanziare" la classe teorica Foo, hai il metodo, istanza (String nameOfFoo) che fa map.put (nameOfFoo, campi) dove i campi sono un HashMap <String, Object> fields.put ("a", nuovo int ( "5")); fields.put ("b", new int ("6")); fields.put ("c", "blah"));
George Xavier,

Risposte:


307

C'è molto di più nelle classi astratte rispetto alle implementazioni dei metodi predefiniti (come lo stato privato), ma a partire da Java 8, ogni volta che hai la scelta di uno dei due, dovresti andare con il defaultmetodo defender (aka. ) Nell'interfaccia.

Il vincolo sul metodo predefinito è che può essere implementato solo in termini di chiamate ad altri metodi di interfaccia, senza alcun riferimento allo stato di una particolare implementazione. Quindi il caso d'uso principale è metodi di livello superiore e convenienza.

La cosa positiva di questa nuova funzionalità è che, dove prima eri costretto a utilizzare una classe astratta per i metodi di convenienza, vincolando così l'implementatore alla singola eredità, ora puoi avere un design davvero pulito con solo l'interfaccia e un minimo di implementazione sforzo forzato sul programmatore.

La motivazione originale per introdurre defaultmetodi in Java 8 era il desiderio di estendere le interfacce di Collections Framework con metodi orientati al lambda senza interrompere alcuna implementazione esistente. Sebbene ciò sia più rilevante per gli autori delle biblioteche pubbliche, potresti trovare utile la stessa funzione anche nel tuo progetto. Hai un posto centralizzato in cui aggiungere nuova comodità e non devi fare affidamento su come appare il resto della gerarchia dei tipi.


34
Con questo ragionamento, la prossima cosa che aggiungerebbero sono le dichiarazioni del metodo predefinito. Non sono ancora sicuro di questo, la funzione mi sembra più un trucco che viene esposto a tutti per uso improprio.
Pantera,

3
L'unico uso di Classi astratte nell'era Java 8 che posso vedere è per la definizione di campi non finali. In Interfaces i campi sono di default finali quindi non puoi cambiarli una volta assegnati.
Anuroop,

7
@Anuroop Non solo per impostazione predefinita --- questa è l'unica opzione. Le interfacce non possono dichiarare lo stato di istanza, motivo per cui le classi astratte sono qui per rimanere.
Marko Topolnik,

2
@PhilipRego I metodi astratti non chiamano nulla perché non hanno implementazione. I metodi implementati in una classe possono accedere allo stato della classe (variabili di istanza). Le interfacce non possono dichiararle, quindi i metodi predefiniti non possono accedervi. Devono fare affidamento sulla classe che fornisce un metodo implementato che accede allo stato.
Marko Topolnik,

2
Marko Topolnik, la tua risposta è morta. Ma vorrei raccomandare un aggiornamento alla tua risposta. È possibile aggiungere che la bellezza dei metodi predefiniti è che, se l'interfaccia aggiunge nuovi metodi predefiniti, l'implementazione precedente di tale interfaccia non si interromperà. Questo non era vero prima di Java 8.
hfontanez,

125

Ci sono alcune differenze tecniche. Le classi astratte possono ancora fare di più rispetto alle interfacce Java 8:

  1. La classe astratta può avere un costruttore.
  2. Le classi astratte sono più strutturate e possono contenere uno stato.

Concettualmente, lo scopo principale dei metodi di difesa è una retrocompatibilità dopo l'introduzione di nuove funzionalità (come funzioni lambda) in Java 8.


20
Questa risposta è in realtà corretta e ha senso soprattutto "Concettualmente, lo scopo principale dei metodi di difesa è una retrocompatibilità"
Prova del

1
@UnKnown questa pagina fornisce maggiori informazioni: docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
bernie

@UnKnown, in sostanza ti consente di aggiungere metodi a un'interfaccia e le classi che implementano tale interfaccia ottengono automaticamente quella funzionalità.
LegendLength,

3
Un punto più sottile sul punto n. 2 sopra "può contenere lo stato è questo". Le classi astratte possono contenere uno stato che può essere modificato in seguito. Le interfacce possono anche mantenere lo stato, ma una volta assegnato uno stato dopo la creazione dell'istanza, lo stato non può essere modificato.
Anuroop,

3
@Anuroop Non descriverei i public static finalcampi di un'interfaccia come "stato". La staticparte indica che non sono affatto correlati a un'istanza particolare. Sono assegnati all'istanza della classe , che non è la stessa della creazione dell'istanza successiva .
Geronimo,

65

Questo è stato descritto in questo articolo . Pensa alle forEachcollezioni.

List<?> list = 
list.forEach(…);

ForEach non è ancora dichiarato java.util.Listjava.util.Collectiondall'interfaccia. Una soluzione ovvia sarebbe semplicemente aggiungere il nuovo metodo all'interfaccia esistente e fornire l'implementazione laddove richiesto nel JDK. Tuttavia, una volta pubblicato, è impossibile aggiungere metodi a un'interfaccia senza interrompere l'implementazione esistente.

Il vantaggio offerto dai metodi predefiniti è che ora è possibile aggiungere un nuovo metodo predefinito all'interfaccia e non interrompere le implementazioni.


1
"è impossibile aggiungere metodi a un'interfaccia senza interrompere l'implementazione esistente" - vero?
Andrey Chaschev,

26
@AndreyChaschev Se si aggiunge un nuovo metodo all'interfaccia, tutti gli implementatori devono implementare quel nuovo metodo. Pertanto interrompe le implementazioni esistenti.
Marko Topolnik,

4
@MarkoTopolnik grazie, l'ho perso. Solo per citare c'è un modo per evitarlo parzialmente, presentando questo metodo in un'implementazione astratta di default. Per questo esempio questo sarebbe AbstractList::forEachlanciare un UnsupportedOperationException.
Andrey Chaschev,

3
@AndreyChaschev Sì, quello era il vecchio modo (khm ... è il modo attuale :), con la mancanza che limita l'implementatore alla singola eredità dall'implementazione astratta fornita.
Marko Topolnik,

Non mi romperò se ciò accade in anticipo, tutte le implementazioni includevano quel metodo. Il che è improbabile ma possibile.
George Xavier,

19

Questi due sono abbastanza diversi:

I metodi predefiniti prevedono l' aggiunta di funzionalità esterne alle classi esistenti senza modificarne lo stato.

E le classi astratte sono un normale tipo di eredità, sono normali classi che si intende estendere.


18

Come descritto in questo articolo,

Classi astratte contro interfacce in Java 8

Dopo aver introdotto il metodo predefinito, sembra che le interfacce e le classi astratte siano uguali. Tuttavia, sono ancora concetto diverso in Java 8.

La classe astratta può definire il costruttore. Sono più strutturati e possono avere uno stato ad essi associato. Al contrario, il metodo predefinito può essere implementato solo in termini di invocazione di altri metodi di interfaccia, senza alcun riferimento allo stato di una particolare implementazione. Quindi, entrambi usano per scopi diversi e la scelta tra due dipende davvero dal contesto dello scenario.


Credo che la classe astratta abbia un costruttore che può essere definito diversamente dall'interfaccia. Anche in Java 8 sono entrambi diversi l'uno dall'altro a causa di ciò.
Hemanth Peela,

1
perché una classe astratta ha un costruttore se non può essere istanziata?
George Xavier,

Possiamo chiamare super () dalla classe figlio che chiamerà costruttore di classe astratta. Ciò influisce sullo stato della classe astratta.
Sujay Mohan,

14

Per quanto riguarda la tua richiesta di

Quindi, quando utilizzare l'interfaccia con i metodi predefiniti e quando utilizzare una classe astratta? Le classi astratte sono ancora utili in quello scenario?

la documentazione java fornisce una risposta perfetta.

Classi astratte rispetto alle interfacce:

Le classi astratte sono simili alle interfacce. Non è possibile creare un'istanza e possono contenere una combinazione di metodi dichiarati con o senza un'implementazione.

Tuttavia, con le classi astratte, è possibile dichiarare campi non statici e definitivi e definire metodi concreti pubblici, protetti e privati.

Con le interfacce, tutti i campi sono automaticamente pubblici, statici e finali e tutti i metodi dichiarati o definiti (come metodi predefiniti) sono pubblici. Inoltre, è possibile estendere solo una classe, indipendentemente dal fatto che sia astratta, mentre è possibile implementare un numero qualsiasi di interfacce.

Casi d'uso per ciascuno di essi sono stati spiegati nel seguente post SE:

Qual è la differenza tra un'interfaccia e una classe astratta?

Le classi astratte sono ancora utili in quello scenario?

Sì. Sono ancora utili. Possono contenere metodi e attributi non statici, non definitivi ( protetti, privati ​​oltre al pubblico ), cosa impossibile anche con le interfacce Java-8.


Ora anche le interfacce hanno metodi privati howtodoinjava.com/java9/java9-private-interface-methods
valijon

13

Ogni volta che abbiamo una scelta tra la classe astratta e l'interfaccia dovremmo sempre (quasi) preferire metodi predefiniti (noti anche come difensore o estensioni virtuali).

  1. I metodi predefiniti hanno messo fine al modello classico di interfaccia e una classe complementare che implementa la maggior parte o tutti i metodi in tale interfaccia. Un esempio è Collection and AbstractCollection. Ora dovremmo implementare i metodi nell'interfaccia stessa per fornire funzionalità predefinite. Le classi che implementano l'interfaccia possono scegliere di sovrascrivere i metodi o ereditare l'implementazione predefinita.
  2. Un altro uso importante dei metodi predefiniti è interface evolution. Supponiamo di avere un ballo di classe come:

    public class Ball implements Collection { ... }

Ora in Java 8 viene introdotta una nuova funzionalità. Possiamo ottenere uno stream usando il streammetodo aggiunto all'interfaccia. Se streamnon fosse un metodo predefinito, tutte le implementazioni per l' Collectioninterfaccia si sarebbero guastate in quanto non avrebbero implementato questo nuovo metodo. L'aggiunta di un metodo non predefinito a un'interfaccia non lo è source-compatible.

Ma supponiamo di non ricompilare la classe e di utilizzare un vecchio file jar che contiene questa classe Ball. La classe verrà caricata correttamente senza questo metodo mancante, è possibile creare istanze e sembra che tutto funzioni correttamente. MA se il programma invoca il streammetodo su istanza di Ballotterremo AbstractMethodError. Quindi, rendendo il metodo predefinito risolto entrambi i problemi.


9

I metodi predefiniti nell'interfaccia Java consentono l' evoluzione dell'interfaccia .

Data un'interfaccia esistente, se si desidera aggiungere un metodo senza interrompere la compatibilità binaria con le versioni precedenti dell'interfaccia, sono disponibili due opzioni: aggiungere un metodo predefinito o statico. In effetti, qualsiasi metodo astratto aggiunto all'interfaccia dovrebbe essere implementato dalle classi o dalle interfacce che implementano questa interfaccia.

Un metodo statico è unico per una classe. Un metodo predefinito è univoco per un'istanza della classe.

Se si aggiunge un metodo predefinito a un'interfaccia esistente, le classi e le interfacce che implementano questa interfaccia non devono implementarlo. Loro possono

  • implementare il metodo predefinito e sovrascrive l'implementazione nell'interfaccia implementata.
  • dichiarare nuovamente il metodo (senza implementazione) che lo rende astratto.
  • non fare nulla (quindi il metodo predefinito dall'interfaccia implementata viene semplicemente ereditato).

Maggiori informazioni sull'argomento qui .


7

Anche se è una vecchia domanda, lasciami dare anche il mio contributo.

  1. classe astratta: all'interno della classe astratta possiamo dichiarare le variabili di istanza, che sono richieste alla classe figlio

    Interfaccia: all'interno dell'interfaccia ogni variabile è sempre statica pubblica e finale non possiamo dichiarare variabili di istanza

  2. classe astratta: la classe astratta può parlare dello stato dell'oggetto

    Interfaccia: l' interfaccia non può mai parlare di stato dell'oggetto

  3. classe astratta: All'interno della classe astratta possiamo dichiarare i costruttori

    Interfaccia: all'interno dell'interfaccia non possiamo dichiarare i costruttori poiché lo scopo dei
    costruttori è di inizializzare le variabili di istanza. Quindi qual è la necessità del costruttore lì se non possiamo avere variabili di istanza nelle interfacce .

  4. classe astratta: All'interno della classe astratta possiamo dichiarare blocchi di istanza e statici

    Interfaccia: le interfacce non possono avere istanze e blocchi statici.

  5. classe astratta: la classe astratta non può fare riferimento all'espressione lambda

    Interfacce: le interfacce con un singolo metodo astratto possono fare riferimento all'espressione lambda

  6. classe astratta : All'interno della classe astratta possiamo ignorare i metodi della CLASSE OGGETTO

    Interfacce: non è possibile ignorare i metodi della CLASSE OGGETTO all'interno delle interfacce.

Concluderò sulla nota che:

I concetti del metodo predefinito / i concetti del metodo statico nell'interfaccia sono venuti solo per salvare le classi di implementazione ma non per fornire un'implementazione utile significativa. I metodi predefiniti / metodi statici sono una sorta di implementazione fittizia, "se vuoi puoi usarli o puoi sovrascriverli (nel caso di metodi predefiniti) nella classe di implementazione" In questo modo salvandoci dall'implementazione di nuovi metodi nelle classi di implementazione ogni volta che nuovi metodi nelle interfacce sono aggiunti. Pertanto le interfacce non possono mai essere uguali a classi astratte.


5

La regola di Remi Forax è che non progetti con le classi astratte. Progetta la tua app con interfacce . Watever è la versione di Java, qualunque sia la lingua. Si è sostenuto dalla I nterface principio della segregazione in SOL I D principi.

Successivamente puoi utilizzare le classi astratte per fattorizzare il codice. Ora con Java 8 puoi farlo direttamente nell'interfaccia. Questa è una struttura, non di più.


2

quando utilizzare l'interfaccia con i metodi predefiniti e quando utilizzare una classe astratta?

Compatibilità con le versioni precedenti: immagina che la tua interfaccia sia implementata da centinaia di classi, la modifica di tale interfaccia costringerà tutti gli utenti a implementare il metodo appena aggiunto, anche se potrebbe non essere essenziale per molte altre classi che implementano la tua interfaccia, inoltre consente l'interfaccia essere un'interfaccia funzionale

Fatti e restrizioni:

1-Può essere dichiarato solo all'interno di un'interfaccia e non all'interno di una classe o di una classe astratta.

2-Deve fornire un corpo

3-Non si presume che sia astratto come altri metodi normali utilizzati in un'interfaccia.


1

In Java 8, un'interfaccia si presenta come una classe astratta, sebbene possano esserci alcune differenze come:

1) Le classi astratte sono classi, quindi non sono limitate ad altre restrizioni dell'interfaccia in Java, ad esempio la classe astratta può avere lo stato , ma non è possibile avere lo stato sull'interfaccia in Java.

2) Un'altra differenza semantica tra l'interfaccia con i metodi predefiniti e la classe astratta è che puoi definire i costruttori all'interno di una classe astratta , ma non puoi definire il costruttore all'interno dell'interfaccia in Java


Sono d'accordo con il n. 2 ma per il n. 1, non puoi semplicemente implementare l'interfaccia e quindi avere uno stato tramite la classe di implementazione?
George Xavier,

0

I metodi predefiniti nell'interfaccia Java devono essere utilizzati di più per fornire un'implementazione fittizia di una funzione, salvando così qualsiasi classe di implementazione di tale interfaccia dal dolore di dichiarare tutti i metodi astratti anche se vogliono gestirne solo uno. I metodi predefiniti nell'interfaccia sono quindi in qualche modo più un sostituto del concetto di classi di adattatori.

Si suppone tuttavia che i metodi in classe astratta forniscano un'implementazione significativa che qualsiasi classe figlio dovrebbe sovrascrivere solo se necessario per sovrascrivere una funzionalità comune.


0

Come menzionato in altre risposte, è stata aggiunta la possibilità di aggiungere l'implementazione a un'interfaccia al fine di fornire la compatibilità con le versioni precedenti nel framework Collezioni. Direi che fornire compatibilità con le versioni precedenti è potenzialmente l'unica buona ragione per aggiungere l'implementazione a un'interfaccia.

Altrimenti, se aggiungi l'implementazione a un'interfaccia, stai infrangendo la legge fondamentale per cui le interfacce sono state aggiunte in primo luogo. Java è un unico linguaggio ereditario, a differenza del C ++ che consente l'ereditarietà multipla. Le interfacce offrono i vantaggi di battitura che derivano da un linguaggio che supporta l'ereditarietà multipla senza introdurre i problemi che derivano dall'ereditarietà multipla.

Più specificamente, Java consente solo l'ereditarietà singola di un'implementazione, ma consente l'ereditarietà multipla delle interfacce. Ad esempio, il seguente è un codice Java valido:

class MyObject extends String implements Runnable, Comparable { ... }

MyObject eredita una sola implementazione, ma eredita tre contratti.

Java ha trasmesso eredità multipla dell'implementazione perché l'eredità multipla dell'implementazione comporta una serie di problemi spinosi, che esulano dallo scopo di questa risposta. Sono state aggiunte interfacce per consentire l'ereditarietà multipla dei contratti (ovvero interfacce) senza i problemi dell'ereditarietà multipla dell'implementazione.

A supporto del mio punto, ecco una citazione di Ken Arnold e James Gosling del libro The Java Programming Language, 4a edizione :

La singola eredità preclude alcuni progetti utili e corretti. I problemi dell'ereditarietà multipla derivano dall'ereditarietà multipla dell'implementazione, ma in molti casi l'ereditarietà multipla viene utilizzata per ereditare una serie di contratti astratti e forse un'implementazione concreta. Fornire un mezzo per ereditare un contratto astratto senza ereditare un'implementazione consente i vantaggi di battitura dell'eredità multipla senza i problemi dell'eredità multipla dell'implementazione. L'eredità di un contratto astratto è definita eredità dell'interfaccia . Il linguaggio di programmazione Java supporta l'ereditarietà dell'interfaccia consentendo di dichiarare un interfacetipo


-1

Si prega di pensare prima al principio aperto / chiuso. I metodi predefiniti nelle interfacce VIOLANO. Questa è una cattiva funzionalità in Java. Incoraggia la cattiva progettazione, la cattiva architettura, la bassa qualità del software. Suggerirei di evitare di utilizzare completamente i metodi predefiniti.

Ponetevi alcune domande: perché non potete mettere i vostri metodi nella classe astratta? Avresti bisogno di più di una lezione astratta? Quindi pensa a cosa è responsabile la tua classe. Sei sicuro che tutti i metodi che intendi mettere nella singola classe realizzino davvero lo stesso scopo? Potresti distinguere diversi scopi e quindi dividere la tua classe in più classi, per ogni scopo la sua classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.