2015-09-06 17 views
59

Si consideri il seguente codice di esempioPerché Java associa le variabili al momento della compilazione?

class MyClass { 
    public String var = "base"; 

    public void printVar() { 
     System.out.println(var); 
    } 
} 

class MyDerivedClass extends MyClass { 
    public String var = "derived"; 

    public void printVar() { 
     System.out.println(var); 
    } 
} 

public class Binding { 
    public static void main(String[] args) { 
     MyClass base = new MyClass(); 
     MyClass derived = new MyDerivedClass(); 

     System.out.println(base.var); 
     System.out.println(derived.var); 
     base.printVar(); 
     derived.printVar(); 
    } 
} 

dà il seguente risultato

base 
base 
base 
derived 

chiamate di metodo vengono risolte in fase di esecuzione e il metodo override corretto è chiamato, come previsto.
L'accesso alle variabili è invece risolto in fase di compilazione come ho appreso in seguito. mi aspettavo un'uscita come

base 
derived 
base 
derived 

perché nella classe derivata la ridefinizione var ombre uno nella classe base.
Perché il binding delle variabili avviene in fase di compilazione e non in fase di runtime? È solo per motivi di prestazioni?

+3

Una nota a margine per un linguaggio simile: in C# avrai tempo di compilazione vincolante in tutti i casi, a meno di utilizzare specificatamente 'virtuale 'e' override'. E non puoi usarli per le variabili. – edc65

+0

Correlati: http://stackoverflow.com/questions/13748124/why-java-polymorphism-not-work-in-my-example – user11153

risposta

44

Il motivo è spiegato nel linguaggio Java Specification in un esempio in Section 15.11, citato qui di seguito:

...

L'ultima riga mostra che, in effetti, il campo a cui si accede fa non dipende dalla classe runtime dell'oggetto referenziato; anche se s contiene un riferimento a un oggetto della classe T, l'espressione si riferisce al s.xx campo di classe S, perché il tipo dell'espressione s è S. Gli oggetti di classe T contengono due campi denominati x, uno per la classe T e uno per la sua superclasse S.

Questa mancanza di ricerca dinamico per il campo accessi permette ai programmi di essere eseguiti in modo efficiente con le implementazioni semplici. Il potere di ritardo primario vincolante ed è disponibile, ma solo quando metodi di istanza sono utilizzati ...

Quindi sì prestazioni è una ragione. La specifica di come l'espressione di accesso campo viene valutata è indicato come segue:

  • Se il campo non è static:

    ...

    • Se il campo è un non-vuoto final, allora il risultato è il valore del campo membro nominato nel tipo T trovato nell'oggetto a cui fa riferimento il valore del Primaria.

dove primaria nel tuo caso si riferisce la variabile derived che è di tipo MyClass.

Un altro motivo, come suggerito da @Clashsoft, è che nelle sottoclassi i campi non sono sovrascritti, sono nascosti. Quindi ha senso consentire a quali campi accedere in base al tipo dichiarato o utilizzare un cast. Questo vale anche per i metodi statici. Questo è il motivo per cui il campo viene determinato in base al tipo dichiarato. A differenza dell'override dei metodi di istanza in cui dipende dal tipo effettivo. La citazione di cui sopra JLS infatti menziona questo motivo implicitamente:

Il potere di ritardo primario vincolante ed è disponibile, ma solo quando si utilizzano metodi di istanza.

25

Anche se si ha ragione sulle prestazioni, c'è un altro motivo per cui i campi non vengono inviati dinamicamente: non si sarebbe in grado di accedere al campo MyClass.var se si dispone di un'istanza MyDerivedClass.

In genere, non conosco alcun linguaggio tipicamente statico che abbia effettivamente una risoluzione variabile dinamica. Ma se si ha realmente bisogno, è possibile effettuare getter o metodi di accesso (che dovrebbe essere fatto nella maggior parte dei casi, per evitare public campi, in ogni caso):

class MyClass 
{ 
    private String var = "base"; 

    public String getVar() // or simply 'var()' 
    { 
     return this.var; 
    } 
} 

class MyDerivedClass extends MyClass { 
    private String var = "derived"; 

    @Override 
    public String getVar() { 
     return this.var; 
    } 
} 
+4

Molto meglio della mia risposta sarebbe stata: perché il linguaggio è stato progettato in questo modo. – Andreas

+2

"Non sarebbe possibile accedere al campo MyClass.var se si dispone di un'istanza MyDerivedClass." Lo stesso vale per i metodi, quindi questa non è una vera ragione. –

+1

@JensSchauder true, ma per i metodi si sa sempre che si sovrascrive l'altro metodo, e questa è * la funzione chiave più nota di OO. – Clashsoft

4

Il comportamento polimorfico del linguaggio Java lavora con metodi e non membro variabili: hanno progettato la lingua per associare le variabili membro in fase di compilazione.

1

In java, questo è di progettazione. Perché la configurazione dei campi da risolvere dinamicamente renderebbe le cose un po 'più lente. E in realtà, non c'è alcuna ragione per farlo. Poiché puoi rendere i tuoi campi in qualsiasi classe privato e accedervi con i metodi che sono risolti dinamicamente.

Quindi, i campi sono fatti per risolvere meglio a momento della compilazione invece :)