Traduzione di un articolo scritto da Peter Verhas datato aprile 2014. Dal 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.
- cambia l'interfaccia con il metodo abstract m() e aggiungi un'implementazione predefinita.
- Compilare l'interfaccia modificata.
- Esegui classe: errore.
Codice di esempio
Per 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 metodomain
in 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$
GO TO FULL VERSION