Leggera confusione per quanto riguarda la prevalenza delle variabili

Mi sto preparando per SCJP (recentemente ribattezzato come OCPJP da Oracle) e una particolare domanda che ho sbagliato su un simulato esame mi ha confuso, la descrizione della risposta non spiega abbastanza le cose.

Questa è la domanda :

class A { int x = 5; } class B extends A { int x = 6; } public class CovariantTest { public A getObject() { return new A(); } public static void main(String[]args) { CovariantTest c1 = new SubCovariantTest(); System.out.println(c1.getObject().x); } } class SubCovariantTest extends CovariantTest { public B getObject() { return new B(); } } 

La risposta è 5 , ma ho scelto 6 .

Comprendo che l’override si applica ai metodi in fase di esecuzione e non alle variabili, ma al modo in cui la mia mente ha interpretato che println era:

  1. chiama getObject su c1
  2. c1 è in realtà un object SubCovariantTest e ha un override valido per getObject() , quindi utilizzare il metodo SubCovariantTest override
  3. L’override restituisce B, quindi prendi x da B che è 6

È un caso che la JVM ignori la parte getObject() e prenda sempre x da c1 quando le variabili sono associate al momento della compilazione?

Sebbene l’override sia eseguito correttamente per SubCovariantTest, la risposta è 5 a causa di come viene dichiarata la variabile c1. È dichiarato come CovariantTest e non come SubCovariantTest.

Quando viene eseguito c1.getObject (). X, non sa che si tratta di un SubCovariantTest (non è stato utilizzato alcun casting). Questo è il motivo per cui 5 viene restituito da CovariantTest e non 6 da SubCovariantTest.

Se cambi

 System.out.println(c1.getObject().x); 

a

 System.out.println(((SubCovariantTest) c1).getObject().x); 

ne otterrai 6 come previsto.

Modifica: come indicato nei commenti

“I campi non sono polimorfici in Java, solo i metodi sono. La x nella sottoclass nasconde la x nella class base, ma non la sovrascrive.” (Grazie a JB Nizet)

Il termine tecnico per ciò che sta accadendo qui è “nascosto”. I nomi delle variabili in Java sono risolti dal tipo di riferimento, non dall’object a cui fanno riferimento.

  • Un object ha una variabile Ax.
  • L’object B ha entrambe le variabili Ax e Bx.

Tuttavia, i metodi di istanza con la stessa firma sono “sovrascritti” e non “nascosti” e non è ansible accedere alla versione di un metodo che viene sovrascritto dall’esterno.

Si noti che l’occultamento si applica anche ai metodi statici con la stessa firma.

La tua domanda fittizia in una forma semplificata (senza sovrascrivere):

 class A { int x = 5; } class B extends A { int x = 6; } public class CovariantTest { public static void main(String[] args) { A a = new B(); B b = new B(); System.out.println(ax); // prints 5 System.out.println(bx); // prints 6 } } 

Stai chiamando il metodo da c1 : System.out.println(c1.getObject().x);

il tipo di riferimento c1 è:

 public class CovariantTest { public A getObject() { return new A(); } public static void main(String[]args) { CovariantTest c1 = new SubCovariantTest(); System.out.println(c1.getObject().x); } } 

quindi per questo: c1.getObject() tipo di ritorno è A da A si ottiene direttamente l’attributo non il metodo, come si menziona java non sovrascrive gli attributi, quindi sta acquisendo x da A

Va bene, so che è un po ‘tardi per rispondere a questa domanda, ma io e il mio amico abbiamo avuto lo stesso problema e le risposte già qui non lo chiariscono per noi. Quindi dirò solo che problema ho avuto e come ha senso ora 🙂

Ora capisco che i campi non vengono sovrascritti ma invece vengono nascosti come indicato da miller.bartek e capisco anche che l’override è per i metodi e non per i campi, come sottolinea Scott.

Il problema che avevo comunque era questo. Secondo me,

 c1.getObject().x 

Questo deve trasformarsi in:

 new B().x // and not newA().x since getObject() gets overrided 

E quello valuta a 6.

E non sono riuscito a capire perché la variabile di class A (super-class) sia chiamata da un object di class B (sottoclass) senza aver chiesto esplicitamente tale comportamento.

E supponendo dalla formulazione della domanda, sento che l’OP aveva in mente la stessa domanda / dubbio.


La mia risposta:

Ottieni un suggerimento dalla risposta di Elbek. Inserisci le seguenti righe nel metodo principale e prova a compilare il codice:

 A a = c1.getObject(); //line 1 B b = c1.getObject(); //line 2 

Noterai che la riga 1 è completamente legale mentre la riga 2 fornisce un errore di compilazione.

Quindi, quando viene chiamata la funzione getObject (), la funzione CovariantTest (super) viene sovrascritta dalla funzione SubCovariantTest (sub) poiché è valida la sovrascrittura nel codice e c1.getObject () restituirà il nuovo B ().

Tuttavia, poiché la super-funzione restituisce un riferimento di tipo di class A, anche dopo essere stato sovrascritto, deve restituire un riferimento di tipo A di class a meno che naturalmente non lo digitiamo. E qui, la class B è una class A (dovuta all’eredità).

Quindi, praticamente, quello che stiamo ottenendo da c1.getObject () non lo è

 new B() 

ma questo:

 (A) new B() 

Questo è il motivo per cui l’output risulta essere 5 anche se viene restituito un object di class B e la class B ha il valore di x come 6.

Quando i metodi vengono sovrascritti, vengono chiamati i metodi di sottoclass e quando le variabili vengono sostituite vengono utilizzate le variabili della superclass

Quando la class figlio e genitore hanno entrambi una variabile con lo stesso nome, la variabile della class figlio nasconde la variabile della class genitore e questo è chiamato “hide le variabili”.

Mentre il hide le variabili sembra sovrascrivere una variabile simile alla sovrascrittura del metodo, ma non lo è, l’override è applicabile solo ai metodi mentre hide è una variabile applicabile.

Nel caso del metodo che sovrascrive, i metodi sovrascritti sostituiscono completamente i metodi ereditati, quindi quando proviamo ad accedere al metodo dal riferimento del genitore tenendo l’object figlio, viene chiamato il metodo dalla class figlio.

Ma nella class figlio nascosta a variabili nasconde le variabili ereditate invece di rimpiazzarle, così quando proviamo ad accedere alla variabile dal riferimento del genitore tenendo l’object figlio, sarà ansible accedervi dalla class genitore.

Quando una variabile di istanza in una sottoclass ha lo stesso nome di una variabile di istanza in una superclass, la variabile di istanza viene scelta dal tipo di riferimento.

Puoi leggere di più su What is Variable Shadowing e Hiding in Java.