2016-01-14 36 views
5

Sto cercando di capire cosa succede se un metodo è presente sia in una classe astratta che in un'interfaccia. Di seguito, ho pubblicato uno scenario che ti dà un'idea chiara di ciò a cui mi riferisco.Implementazione di un metodo presente sia nell'interfaccia che nella classe astratta in java

interface act 
{ 
    void read(); 
} 

abstract class enact 
{ 
    void read() { 
     System.out.println("hello"); 
    } 
} 

public class second extends enact implements act 
{ 
    public void read() { 

     // does method read() correspond to Interface or abstract class? 
    } 
} 

Sorprendentemente, non c'è errore di compilazione quando scrivo questo codice. Qualsiasi suggerimento sarebbe molto utile.

+0

"Sorprendentemente, non c'è errore di compilazione" perché dovrebbe essere? Quali problemi ti aspettavi di affrontare quando si utilizza tale codice? – Pshemo

+0

@Pshemo Sono solo curioso di sapere se il metodo read() si riferisce all'interfaccia o alla classe astratta. È una situazione di ambiguità. Voglio sapere come il compilatore ha risolto questa ambiguità –

+0

"* Sono solo curioso di sapere se il metodo è stato letto() si riferisce a ... * "define" si riferisce a ". – Pshemo

risposta

1

io sono solo curioso di sapere se il metodo read() si riferisce a interfaccia o classe astratta

o nessuno, davvero. Dipende dal tipo apparente nel contesto in cui effettivamente lo usi. Per esempio. se fare

enact example = new second(); 

o

act example = new second(); 

Entrambi permettono di compilare il codice:

example.read(); 

perché il metodo read() è definita in entrambi i tipi apparenti, agire e attuare. Ma in entrambi i casi, è la definizione read() definita nella "seconda" classe che conta davvero. Questo è ciò che viene eseguito.

1

Non sono sicuro del tipo di problemi previsti, quindi cercherò di dimostrare che non ci sono problemi con questo scenario.

Si sta chiamando i metodi da riferimenti come:

Second sc = new Second();//you should name your classes with upper-case 
sc.read(); 

e questo vi eseguire codice da Second classe, perché questo è il tipo di oggetto immagazzinate nellasc di riferimento (questo è come funziona il polimorfismo - o per sii preciso dynamic binding).

Si potrebbe anche creare un'istanza di Second classe e passarlo al riferimento di Act tipo (tutti i tipi in Java dovrebbe iniziare con maiuscole, comprese le interfacce ed enumeratori) come

Act act = sc;// or Act act = new Second(); 
act.read(); 

Poiché read() metodo è polimorfico (solo i metodi finali, privati ​​o statici non lo sono) in fase di esecuzione JVM cercherà il codice per eseguire in classe quale istanza è memorizzata nell'interfaccia art, quindi poiché è l'istanza del codice classe Second di read() da quella classe verrà eseguita (se non lo sovrascrivere in quel codice classe ereditato da Enact abstrac t classe sarà eseguita).

1

La mia comprensione è la seguente. Iniziamo considerando un'impostazione più semplice. Class Second è astratta e implementa due interfacce, Act e React. Entrambe le interfacce dichiarano un metodo void read() non predefinito. In questo caso, Second eredita più dichiarazioni dello stesso metodo, nessuna implementazione e il compilatore è felice (JLS 8.4.8.4).

interface Act { 

    void read(); 
} 

interface React { 

    void read(); 
} 

public abstract class Second implements Act, React { 
} 

Se uno o entrambi i metodi di interfaccia sono metodi predefiniti, otteniamo un errore di compilazione (JLS 8.4.8.4, 9.4.1.3), a meno che non Seconda eredita il metodo() da una superclasse (JLS 8.4.8 una lettura astratta .4), come nel seguente scenario, dove Second effettivamente finisce per ereditare tre metodi read().

interface Act { 

    default void read() {} 
} 

interface React { 

    void read(); 
} 

abstract class First { 

    abstract void read(); 
} 

public abstract class Second extends First implements Act, React { 
} 

In assenza di tale superclasse, Seconda deve dare un'implementazione di read(), impedendo così l'eredità dei metodi contrastanti (GLS 9.4.1.3). Naturalmente, Second deve fare lo stesso se tutti i metodi di interfaccia sono astratti e non vogliamo dichiarare la classe stessa come astratta.

Se Second dà un'implementazione concreta di read(), come nell'esempio seguente, questo metodo non "si riferisce" a uno o l'altro dei metodi di interfaccia: semplicemente sovrascrive e impedisce l'ereditarietà di qualsiasi e tutti i metodi di superinterfaccia con la stessa firma - più precisamente, con una firma che la firma di read() in Second è una sottosegnalazione di - come se esistesse un solo metodo (JLS 8.4.8.1). (In alcuni casi angusti potrebbe essere impossibile per un metodo soddisfare simultaneamente tutti i contratti dei metodi che intende sovrascrivere. James Gosling ne offre un buon esempio nella sezione 4.3.2 dei suoi - e colleghi - il linguaggio di programmazione Java.)

interface Act { 

    void read(); 
} 

interface React { 

    void read(); 
} 

public class Second implements Act, React { 

    @Override 
    public void read() {} 
} 

il tuo caso è simile.

interface Act { 

    void read(); 
} 

abstract class Enact { 

    void read() {} 
} 

public class Second extends Enact implements Act { 

    @Override 
    public void read() {} 
} 

L'unica vera differenza è che read() in Enact è un metodo concreto (il fatto che Enact è astratta è irrilevante), ma le cose non cambiano molto: la dichiarazione in Second prevale sia in lettura () in Act e read() in Enact (JLS 8.4.8.1). Allo stesso tempo, è un'implementazione valida dell'interfaccia Act, quindi non c'è davvero alcun problema qui.

Si noti che questo codice potrebbe essere compilato anche se Second non ha sovrascritto read(): Second erediterà read() da Enact e il metodo ereditato sarebbe considerato un'attuazione altrettanto valida di Act (JLS 8.4.8, 8.4 .8.1, 9.4.1.3).