JavaRush /Java Blog /Random-IT /Metodi predefiniti in Java 8: cosa possono e non possono ...
Spitfire
Livello 33

Metodi predefiniti in Java 8: cosa possono e non possono fare?

Pubblicato nel gruppo Random-IT
Traduzione di un articolo scritto da Peter Verhas datato aprile 2014. Metodi predefiniti in Java 8: cosa possono e non possono fare?  -1Dal traduttore: il termine " metodo predefinito " è appena apparso in Java e non sono sicuro che esista una traduzione consolidata in russo per questo. Utilizzerò il termine "metodo predefinito", anche se non penso che sia l'ideale. Ti invito a discutere una traduzione di maggior successo.

Qual è il metodo predefinito

Ora, con il rilascio di Java 8, è possibile aggiungere nuovi metodi alle interfacce in modo che l'interfaccia rimanga compatibile con le classi che la implementano. Questo è molto importante se stai sviluppando una libreria utilizzata da molti programmatori da Kiev a New York. Prima di Java 8, se definivi un'interfaccia in una libreria, non potevi aggiungervi metodi senza rischiare che alcune applicazioni che eseguivano la tua interfaccia si interrompessero quando veniva aggiornata. Quindi in Java 8 non puoi più aver paura di questo? No, non puoi. L'aggiunta di un metodo predefinito a un'interfaccia potrebbe rendere inutilizzabili alcune classi. Diamo prima un'occhiata agli aspetti positivi dei metodi predefiniti. In Java 8 il metodo può essere implementato direttamente nell'interfaccia. (Ora è possibile implementare anche metodi statici in un'interfaccia, ma questa è un'altra storia.) Un metodo implementato in un'interfaccia è chiamato metodo predefinito ed è indicato dalla parola chiave default . Se una classe implementa un'interfaccia, può, ma non è tenuta a, implementare i metodi implementati nell'interfaccia. La classe eredita l'implementazione predefinita. Questo è il motivo per cui non è necessario modificare le classi quando si cambia l'interfaccia che implementano.

Eredità multipla?

Le cose diventano più complicate se una classe implementa più di una (diciamo due) interfacce e implementano lo stesso metodo predefinito. Quale metodo erediterà la classe? La risposta è nessuna. In questo caso, la classe deve implementare il metodo stessa (direttamente o ereditandolo da un'altra classe). La situazione è simile se solo un'interfaccia ha un metodo predefinito e nell'altra lo stesso metodo è astratto. Java 8 cerca di essere disciplinato ed evitare situazioni ambigue. Se i metodi vengono dichiarati in più di un'interfaccia, la classe non eredita alcuna implementazione predefinita: verrà visualizzato un errore di compilazione. Tuttavia, potresti non ricevere un errore di compilazione se la tua classe è già compilata. Java 8 non è abbastanza robusto a questo riguardo. Ci sono ragioni per questo, che non voglio entrare in discussione (ad esempio: la versione Java è già stata rilasciata e il tempo per le discussioni è passato da tempo e in generale non è questa la sede per loro).
  • Diciamo che hai due interfacce e una classe le implementa entrambe.
  • Una delle interfacce implementa il metodo predefinito m().
  • Compili tutte le interfacce e la classe.
  • Puoi modificare un'interfaccia che non ha un metodo m() dichiarandola come metodo astratto.
  • Compila solo l'interfaccia modificata.
  • Inizia la lezione.
Metodi predefiniti in Java 8: cosa possono e non possono fare?  - 2In questo caso la lezione funziona. Non è possibile compilarlo con le interfacce aggiornate, ma è stato compilato con versioni precedenti e quindi funziona. Ora
  • cambia l'interfaccia con il metodo abstract m() e aggiungi un'implementazione predefinita.
  • Compilare l'interfaccia modificata.
  • Esegui classe: errore.
Quando ci sono due interfacce che forniscono un'implementazione predefinita di un metodo, quel metodo non può essere chiamato in una classe a meno che non sia implementato dalla classe stessa (di nuovo, da solo o ereditato da un'altra classe). Metodi predefiniti in Java 8: cosa possono e non possono fare?  - 3Compatibile con la classe. Può essere caricato con un'interfaccia modificata. Potrebbe anche essere eseguito finché non viene chiamato un metodo che ha un'implementazione predefinita in entrambe le interfacce.

Codice di esempio

Metodi predefiniti in Java 8: cosa possono e non possono fare?  - 4Per dimostrare quanto sopra, ho creato una directory di test per la classe C.java e 3 sottodirectory per le interfacce nei file I1.java e I2.java. La directory root per il test contiene il codice sorgente per la classe C.java. La directory di base contiene una versione delle interfacce adatta per l'esecuzione e la compilazione: l'interfaccia I1 ha un metodo predefinito m(); L'interfaccia I2 non dispone ancora di metodi. La classe ha un metodo mainin modo che possiamo eseguirlo per testarlo. Controlla se sono presenti argomenti sulla riga di comando, quindi possiamo eseguirlo facilmente con o senza chiamare il comando m().
~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}
Puoi compilare ed eseguire la classe dalla riga di comando.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
La directory compatibile contiene una versione dell'interfaccia I2 che dichiara astratto il metodo m() e anche, per ragioni tecniche, una copia non modificata di I1.java.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
Un tale insieme non può essere utilizzato per compilare una classe C:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error
Il messaggio di errore è molto accurato. Abbiamo però C.class da una compilazione precedente e, se compiliamo le interfacce nella directory compatibile, avremo due interfacce che potranno ancora essere utilizzate per eseguire la classe:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
La terza directory - wrong- contiene la versione I2, che dichiara anche il metodo m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
Non devi nemmeno preoccuparti della compilazione. Anche se il metodo viene dichiarato due volte, la classe può comunque essere utilizzata ed eseguita finché non viene chiamato il metodo m(). Questo è ciò per cui abbiamo bisogno dell'argomento della riga di comando:
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

Conclusione

Quando esegui il porting della tua libreria su Java 8 e modifichi le tue interfacce per includere metodi predefiniti, probabilmente non avrai alcun problema. Almeno, questo è ciò che sperano gli sviluppatori della libreria Java 8 quando aggiungono funzionalità. Le applicazioni che utilizzano la tua libreria la utilizzano ancora per Java 7, dove non esistono metodi predefiniti. Se si utilizzano più librerie insieme, esiste la possibilità di conflitto. Come evitarlo? Progetta l'API della tua libreria nello stesso modo di prima. Non accontentarti facendo affidamento sulle funzionalità dei metodi predefiniti. Sono l'ultima risorsa. Scegli i nomi con attenzione per evitare collisioni con altre interfacce. Vediamo come si svilupperà lo sviluppo per Java utilizzando questa funzionalità.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION