2016-01-04 27 views
24

Oggi sono incappato in qualche strano comportamento di classe interno (non statico).Comportamento membro diverso della classe interna se la classe interna estende la classe esterna?

Se ho le seguenti classi ...

class B { 
    String val = "old"; 

    void run(){ 
     val = "new"; 
     System.out.println(val);  // outputs: new 
     new InnerB().printVal();  // outputs: new 
    } 

    private class InnerB { 
     void printVal(){ System.out.println(val); } 
    } 
} 

new B().run(); 

... tutto sembra essere chiaro. L'istanza di InnerB appartiene all'istanza di B quindi se dovesse restituire val stamperà il valore già sostituito 'new'.

MA se la classe interna estende la classe esterna, questo non funziona.

class B { 
    String val = "old"; 

    void run(){ 
     val = "new"; 
     System.out.println(val);  // outputs: new 
     new InnerB().printVal();  // outputs: new 
     new InheritedB().printVal(); // outputs: old new 
    } 

    private class InnerB { 
     void printVal(){ System.out.println(val); } 
    } 

    private class InheritedB extends B{ 
     void printVal(){ System.out.println(val + " "+ B.this.val); } 
    } 
} 

new B().run(); // outputs: new new old! 

Se ho uno sguardo ai costruttori Vedo anche che una nuova istanza di B verrà creato se si crea un'istanza di InheritedB.

Trovo questo molto strano ... Qualcuno può spiegare perché c'è questa differenza?

+0

Sei sicuro che non intendevi "InheritedB estende InnerB'? – user3707125

+0

Solo per la mia tranquillità, per favore dimmi che non hai intenzione di farlo in codice reale, che stavi solo scherzando per vedere cosa è successo. – jpmc26

risposta

26

Questa linea:

new InheritedB().printVal(); 

crea una nuova istanza di InheritedB, il cui esempio è che contiene l'istanza esistente di B (dove val è "new"). Ma a questo punto ci sono dueval variabili:

  • Quello in istanza esistente di B
  • Quello in caso di InheritedB, che ha un separato val campo

L' il valore della seconda variabile è "old" perché è effettivamente il valore predefinito del campo.

Questa affermazione in InheritedB:

System.out.println(val + " "+ B.this.val); 

stampa il valore della val ereditato dal B, seguita dal valore della val nella "istanza contenente".

Potrebbe essere più semplice di pensare di esso che è riscritta a:

public class B 
{ 
    String val = "old"; 
} 

public class InheritedB extends B { 
    B other; 

    public InheritedB(B other) 
    { 
     this.other = other; 
    } 

    void printVal() { 
     System.out.println(val + " "+ other.val); 
    } 
} 

Poi si sta fondamentalmente in esecuzione:

B original = new B(); 
original.val = "new": 
InheritedB inherited = new InheritedB(original); 
inherited.printVal(); 

Speriamo che si può seguire esattamente quello che sta succedendo lì. Il compilatore è approssimativamente che esegue il codice originale in quel codice.

9

val in InheritedB riferisce al val dalla sua classe di base (super.val), dato che è parte di this.

Se non si eredita dalla classe esterna, val fa riferimento all'ambito della classe esterna (B.this.scope). Tuttavia, dal momento che si eredita, this è più vicino nell'ambito, e quindi nasconde l'ambito esterno.

Dal momento che non hai mai chiamato run()su quello interno this, this.val è ancora old.


Se ho uno sguardo ai costruttori Vedo anche che una nuova istanza di B verrà creato se si crea un'istanza di InheritedB.

Sì; la creazione di una classe derivata creerà sempre un'istanza della sua classe base. Non c'è modo di ereditare da un'istanza esistente.

5

Da InheritedB extends B, creando un'istanza InheritedB concede un attributo val, che è "vecchio" di default per ogni nuova classe B o istanza sottoclasse.

Qui, InheritedB stampe il proprio attributoval, non quello dell'istanza di B inclusa.

4

Nel caso di InheritedB ci sono due variabili chiamate val, quella di B e quella di InheritedB. L'applicazione delle regole di visibilità dà il risultato osservato.

2

La differenza è la classe InnerB non ha il membro val in esso. dove la classe InheritedB estende la classe B e ha una propria copia del membro val.

void run(){ 

    val = "new";  //<--- modifies B's val not InheritedB's val 

    System.out.println(val);  // outputs: new 
    new InnerB().printVal();  // outputs: new 
    new InheritedB().printVal(); // outputs: old new 
} 

Nel blocco di codice precedente, accesso printVal di InnerB del contenitore val membro, quale valore già modificato nel metodo run a valore nuovo.

Ma la copia di val nell'oggetto di InheritedB è ancora "vecchio" valore, non modificato e la funzione printVal utilizza tale valore.