Overriding - Accesso ai membri con riassegnazione di riferimento

0

Recentemente ho attraversato un paio di libri per insegnare a me stesso Java e, fortunatamente, soprattutto per fortuna, ho incontrato pochissime difficoltà. Questo è appena cambiato.

Ho letto una sezione sotto l'ereditarietà e l'intera sottoclasse della superclasse

  • Quando viene creato un nuovo oggetto superclasse, è, come tutti gli oggetti, assegnato un riferimento (superRiferimento in questo esempio)

  • Se viene creato un nuovo oggetto sottoclasse (con la sottoclasse di definizione che estende la superclasse) e quindi il riferimento superReference è impostato per fare riferimento a tale anziché all'oggetto originale, è a mia conoscenza che, poiché il riferimento è fatto per una superclasse, solo i membri definiti dalla superclasse possono accedere dalla sottoclasse.

Primo - è corretto?

Secondo: Se sto sovrascrivendo un metodo e quindi ne ho uno nel super e uno nel sub, e creo un oggetto superclasse e poi assegno il suo riferimento, come ho fatto sopra, a un oggetto sottoclasse, dal principio chiamato qualcosa come Dynamic Method Dispatch , un metodo sovrascritto chiamato dovrebbe accedere in modo predefinito al metodo della sottoclasse giusto?

Bene, la mia domanda è:

Se un riferimento a un oggetto superclasse viene riconfigurato per un oggetto sottoclasse e negherà l'accesso diretto object.member ai membri definiti in sottoclasse, solo i membri di supporto definiti superclasse, come può, se un riferimento superclasse viene riconfigurato per un oggetto sottoclasse, un metodo sottoposto a override si applica alla sottoclasse-oggetto se l'accesso è limitato dalla superclasse-reference-reference

    
posta William Brun 13.05.2013 - 02:55
fonte

2 risposte

1

Se osservi le specifiche della lingua Java ( link ) troverai una distinzione tra il tipo statico e il tipo dinamico di riferimenti a oggetti.

Il tipo statico di qualsiasi riferimento si riferisce al tipo dichiarato nel tuo programma al momento sviluppo .

Il tipo dinamico del riferimento si riferisce al tipo di oggetto che è effettivamente memorizzato all'indirizzo di riferimento in runtime .

Quindi se hai una gerarchia semplice, dove A è la superclasse e B è una sottoclasse di A.

class A {
  public String a = "super";
  void m() {...}
}

class B extends A {
  public String a = "sub";
  @Override
  void m() {...}
}

A refA = new A();
A refB = new B();
B refB2 = new B();
refA.m();
refB.m();
System.out.println(refA.a);
System.out.println(refB.a);
System.out.println(refB2.a);

Ad esempio, il tipo statico di refB è A, ma il suo tipo dinamico è B. Che cosa implica in fase di runtime?

  1. Le variabili di istanza sono associate staticamente , ovvero puoi accedere solo alle variabili di istanza del tipo statico, quindi le prime due istruzioni println () stamperanno "super", poiché A è il tipo statico di refA e refB . L'ultima istruzione println () stamperà invece "sub", perché in questo caso B è il tipo statico.
  2. I metodi sono associati dinamicamente , ovvero il metodo invocato viene prima ricercato nel tipo dinamico del riferimento e se non viene trovato, la gerarchia delle classi viene attraversata fino a quando non viene trovata una definizione del metodo sovrascritto ( questo meccanismo è chiamato dispacciamento dinamico). Ciò significa che nel caso in cui il metodo refA.m () void m () di A venga richiamato e nel caso refB.m (), void m () di B sia invocato.
risposta data 15.05.2013 - 14:05
fonte
0

È necessario distinguere tra il sistema di esecuzione (dinamico) in VM, che contiene istanze effettive di classi e il testo (statico) del programma, che contiene variabili che contengono riferimenti a oggetti. Grazie alle informazioni sul tipo di runtime, un oggetto sa sempre a quale classe appartiene; quando la JVM esegue la primitiva invoke* appropriata, eseguirà volentieri qualsiasi metodo di questa classe. Il testo del programma, d'altra parte, conosce solo un limite superiore sul tipo di qualsiasi oggetto che verrà trattenuto da un riferimento.

L'unica difficoltà con l'accesso ai metodi in un oggetto è ottenere l'emissione del codice operativo corretto. È qui che entra in gioco il compilatore. Il sistema tipo impone dei limiti a ciò che i programmi possono fare ai riferimenti; in particolare, non ti permette di emettere alcuna chiamata al metodo che sarebbe possibile in fase di esecuzione . Invece, consente solo le chiamate al metodo che può dimostrarsi corretto in fase di esecuzione. Perché la logica simbolica è incompleta (disclaimer: questa è una terribile semplificazione!), Nessun compilatore può provare tutte le cose del tuo programma che puoi vedere essere vero. Pertanto è comune entrare in situazioni in cui il programmatore sa che un particolare riferimento sarà di un tipo specifico, ma il compilatore non può seguire la loro logica e proibisce una chiamata che non causerebbe problemi.

Questo è un trade-off che acquista una maggiore correttezza del programma (non consentendo un'intera classe di errori precedentemente molto comuni) al costo di una certa flessibilità. Nota che se sei davvero, davvero sicuro di sapere qualcosa di più del compilatore, puoi lanciare un riferimento a un sottotipo e quindi invocare il metodo che volevi; se hai sbagliato, si verifica un ClassCastException , ma se hai ragione, il metodo viene richiamato dal sistema runtime senza reclami.

    
risposta data 13.05.2013 - 08:31
fonte

Leggi altre domande sui tag