2014-09-11 20 views
7

Sto dando un'occhiata a Java 8 notizie rispetto a 7 e oltre a cose molto interessanti come lambda o la nuova struttura temporale, ho scoperto che una nuova funzionalità (?) È stata introdotta: metodi predefiniti.Perché abbiamo bisogno di metodi predefiniti in Java?

ho trovato il seguente esempio in this article:

public interface Math { 

    int add(int a, int b); 

    default int multiply(int a, int b) { 
     return a * b; 
    } 
} 

sembra molto strano per me. Sopra il codice appare come una classe abstract con un metodo implementato. Quindi, perché introdurre metodi predefiniti in un'interfaccia? Qual è il reale vantaggio di questo approccio?

Nello stesso articolo che ho letto questa spiegazione:

Perché uno dovrebbe voler aggiungere metodi nelle interfacce? Lo faremo perché le interfacce sono troppo strettamente associate alle loro classi di implementazione. non è possibile aggiungere un metodo nell'interfaccia senza rompere la classe implementor. Una volta aggiunto un metodo nell'interfaccia, tutte le sue classi implementate devono dichiarare il corpo del metodo di questo nuovo metodo.

Beh, questo non mi convince affatto. IMHO Credo che quando una classe implementa un'interfaccia in modo obbiettivo deve dichiarare il metodo body per ogni metodo in esso contenuto. Questo è sicuramente un vincolo, ma è anche una conferma della sua "natura" (se capisci cosa intendo ...)

Se hai una logica comune a tutte le classi ereditanti, la inserirai in una classe di implementazione abstract .

Quindi, qual è il vero vantaggio di un metodo predefinito? (Sembra più una soluzione di una nuova funzionalità ...)


UPDATE capisco che questo approccio è per la compatibilità all'indietro, ma ancora non mi convince tanto. Un'interfaccia rappresenta un comportamento che una classe DEVE avere. Quindi una classe che implementa una determinata interfaccia ha sicuramente questo comportamento. Ma se qualcuno può cambiare arbitrariamente l'interfaccia, questo vincolo è rotto. Il comportamento può cambiare in qualsiasi momento ... Ho sbagliato?

+0

Correlato: http://stackoverflow.com/questions/19998454/interface-with-default-methods-vs-abstract-class-in-java-8 e http://stackoverflow.com/questions/20139404/java -8-default-methods-vs-non-abstract-methods-in-abstract-classes – assylias

+0

Puoi 'estendere' solo una classe, ma' implementa 'tutte le interfacce che vuoi. –

risposta

7

Questo è per compatibilità con le versioni precedenti.

Se si dispone di un'interfaccia implementata da altre persone, se si aggiunge un nuovo metodo all'interfaccia tutte le implementazioni esistenti vengono interrotte.

Aggiungendo un nuovo metodo con un'implementazione predefinita si rimane compatibili con le sorgenti con le implementazioni esistenti.

Per un po 'semplice esempio/artificioso che dovrebbe auspicabilmente dimostrare questo diciamo si è creato una libreria:

void drawSomething(Skin skin) { 
} 

interface Skin { 
    Color getColor(); 
    Image getBackgroundImage(); 
} 

Ora si arriva a fare una nuova versione della libreria e che si desidera aggiungere il concetto di colori dei bordi, che è facile da aggiungere all'interfaccia:

interface Skin { 
    Color getColor(); 
    Color getBorderColor(); 
    Image getBackgroundImage(); 
} 

Ma il problema è che ogni singola persona che utilizza la libreria deve tornare indietro attraverso ogni implementazione della pelle singola abbiano mai fatto e aggiungere questo nuovo metodo.

Se invece è stata fornita un'implementazione predefinita a getBorderColor che è stata appena chiamata getColor, allora tutto "funziona".

+0

ok, ma aggiungere il metodo body all'interno di un'interfaccia suona molto strano per me ... – davioooh

+1

@davioooh è strano, ma quando Oracle si trovò a dover affrontare una rottura della compatibilità con Java 8 quando si aggiungevano metodi avanzati lambda alle apis delle raccolte; hanno scelto questo approccio come il male minore. –

+0

Anche in questo caso, questa compatibilità a ritroso vale solo per un metodo, nel caso in cui dobbiamo introdurre un altro metodo? – zerocool

0

Supponiamo che a un certo punto si desideri aggiungere nuove funzionalità nell'interfaccia dichiarata, fino a Java 7, Se si aggiungerà un nuovo metodo in un'interfaccia dichiarata, sarà necessario definire l'implementazione del metodo nelle classi che implementano quell'interfaccia.

In java 8, è possibile aggiungere un metodo predefinito contenente l'implementazione e tutta la classe figlio sarà ereditare il metodo.

Edit: (su domande di aggiornamento)

Un'interfaccia rappresentare un comportamento che una classe deve avere

E ancora rappresentano un comportamento che classe deve avere, la vostra confusione è come si sta definendo comportamento. Tutte le classi di implementazione erediteranno il metodo predefinito e sono inoltre libere di scrivere la propria implementazione. considerare due casi seguenti,

  1. Se la classe di implementazione non fornisce la propria implementazione e semplicemente eredita il metodo predefinito. Se si modifica il comportamento del metodo predefinito nell'interfaccia, l'implementazione delle classi avrà un comportamento aggiornato in quanto erediterà il metodo predefinito, quindi mantiene Un'interfaccia rappresenta un comportamento che una classe DEVE avere.
  2. Se la classe di implementazione fornisce la propria versione del metodo predefinito e se si modificherà il comportamento (solo argomenti) del metodo predefinito. In questo caso, la classe di implementazione avrà due metodi sovraccarichi, uno definito in precedenza e il secondo metodo predefinito ereditato. e se si modifica il comportamento completo (anche gli argomenti con tipo di ritorno), creerà ambiguità nell'implementazione della classe poiché non è possibile sovraccaricare il metodo modificando il tipo di reso e l'implementazione verrà interrotta. di nuovo contiene Un'interfaccia rappresenta un comportamento che una classe DEVE avere.

Esempio:

operazione dati Bulk in collezione è aggiunto Java 8 (Riferimento: http://openjdk.java.net/jeps/107), per implementare che forEach() metodo viene aggiunto in Iterable interfaccia. L'aggiunta del metodo astratto nell'interfaccia Iterable interromperà tutto il codice esistente perché ogni classe deve implementare tale metodo.

Risolvere il problema, a seguito di un'inadempienza forEach() metodo viene aggiunto in Iterable interfaccia,

interface Iterable 
{ 
    default void forEach(Consumer<? super T> action) 
    { 
     for (T t : this) action.accept(t); 
    } 
} 

Riferimento: Java 8 : Default method in Interface

+0

Il tuo riferimento è un po 'obsoleto. Quello che era chiamato 'Block' nelle versioni molto vecchie è stato chiamato' Consumer' per un po 'di tempo. Inoltre, non ha senso dire (correttamente) che 'forEach' è stato aggiunto a' Iterable' ma scrivere codice di esempio definendolo in 'Collection' subito dopo. Vedi http://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer- – Holger

+0

@Holger Non sto ottenendo il punto su 'forEach' , puoi spiegare per favore lil bit !! (non discutendo, prendi questo come un pls positivo) –

+0

@Holger Ohh !!! aggiorna la risposta e aggiornerà anche l'articolo.È stato scritto molto tempo fa (31 marzo 2014), grazie mille :) –

3

Ci sono stati un sacco di metodi che agiscono su un'interfaccia astratta in passato. Prima di Java 8 dovevano essere messi in un'ulteriore classe che accoppiasse l'interfaccia, ad es. Collections con Collection.

Questo vecchio approccio non era né più convincente dei metodi default né più pratico. Invece di list.sort() hai dovuto dire Collections.sort(list). Ciò implica anche che è necessario prendere una decisione fondamentale quando si crea uno interface, o si richiede ad ogni implementazione List di implementare un metodo sort oppure si fornisce un metodo sort in una classe di utilità che non può essere sovrascritto.

Con default metodi che è possibile avere entrambi, un'implementazione standard che i List implementazioni non hanno bisogno di implementare da solo, ma ancora possono essere sovrascritte se un'implementazione concreta ha un modo più efficace per farlo conoscere i suoi interni, per esempio ArrayList.sort passa il suo array interno direttamente a Arrays.sort saltando alcune operazioni intermedie.

Problemi correlati