2014-08-29 7 views
16

An article on classloading afferma che il metodo getClass() non dovrebbe essere chiamato all'interno di un costruttore perché:È sempre sicuro chiamare getClass() all'interno del costruttore della sottoclasse?

inizializzazione oggetto sarà completa solo all'uscita del codice costruttore.

L'esempio hanno dato era:

public class MyClassLoader extends ClassLoader{ 
    public MyClassLoader(){ 
     super(getClass().getClassLoader()); // should not call getClass() because object 
              // initialization will be complete only at 
              // the exit of the constructor code. 
    } 
} 

Tuttavia da quello che so, il metodo finale nativo getClass() sarà sempre restituire l'oggetto di tale istanza oggetto java.lang.Class, indipendentemente dove si chiama (in un raggio il costruttore o no).

Chiamando getClass() all'interno del costruttore sempre ci danno problemi?

In tal caso, quali sono alcuni esempi per cui chiamare il numero getClass() all'interno del costruttore potrebbe darci errori?

risposta

9

Chiamare getClass() all'interno del costruttore ci dà mai problemi? Se sì, quali sono alcuni esempi per cui chiamare getClass() all'interno del costruttore sarebbe darci errori?

Utilizzando getClass() nel costruttore in questo modo sarà sempre risultato in un errore di compilazione, in quanto this non può essere fatto riferimento prima super() è stato chiamato.

Main.java:17: error: cannot reference this before supertype constructor has been called 
     super(getClass().getClassLoader()); // should not call getClass() because object 
      ^
1 error 

È possibile testarlo su http://ideone.com/B0nYZ1.

Il Class è pronto, ma la esempio non può essere utilizzato per fare riferimento alla Class ancora.

Ancora, si possibile utilizzare il riferimento Class nel costruttore, ma devi farlo in un modo leggermente diverso: super(MyClassLoader.class.getClassLoader())

Inoltre, si è liberi di utilizzare getClass() nel costruttore dopo il supertipo il costruttore è stato chiamato - come hai già sottolineato, l'oggetto è fondamentalmente pronto dopo e il riferimento Class può essere dedotto dall'istanza.

+0

Su Eclipse, anche se dice "errore", compila e gira bene. Ho preso il file '.class' compilato e l'ho eseguito manualmente con il comando' java'. **Corre**. – Pacerier

+0

@Pacerier a) Eclipse non è un compilatore, è un IDE; si prega di fornire la versione esatta di 'javac'/JDK e verificare il processo di compilazione chiamando' javac' sull'origine manualmente, b) non si compila, né su Ideone né sulla mia scatola, JDK 8u5, c) Si prega di inviare il esatto '. java' sorgente e file'. class' compilato online per consentire il disassemblaggio. A proposito, spero che tu non abbia copiato il codice che avevo su Ideone - l'ho modificato due volte nel frattempo, una versione effettivamente compilata * perché utilizzava 'MyClassLoader.class' invece di' getClass() ' * ... – vaxquis

+0

Uso il compilatore ECJ di Eclipse e viene compilato in un file .class che è possibile visualizzare all'indirizzo https://drive.google.com/file/d/0B53jM4a9X2fqZHBOcldxV1Vnb2s/edit?usp=sharing. La fonte .java è già nella domanda. – Pacerier

1

La ragione per cui non si dovrebbero chiamare i metodi della classe genitore, almeno in una chiamata super(), è che poiché l'oggetto padre deve ancora essere creato, non c'è modo di sapere se il metodo verrà eseguito correttamente. Devi ricordare che la chiamata avviene prima che i costruttori genitori abbiano la possibilità di preparare i suoi dati.

Dopo un'invocazione super(), si dovrebbe essere abbastanza sicuri di utilizzare i metodi padre, entro limiti ragionevoli, poiché gli oggetti padre hanno tutti completato ciò di cui hanno bisogno per essere considerati pronti per l'uso. Ho visto entro limiti ragionevoli perché i metodi genitore possono chiamare metodi figlio sovrascritti che è necessario garantire che siano sicuri da chiamare durante la costruzione.

Unsafe:

public static class Base { 
    public final void print() { 
     System.out.println(this.get()); 
    } 

    public int get() { 
     return 2; 
    } 
} 

public static final class Sub extends Base { 

    private int x; 

    public Sub() { 
     super(); 
     this.print(); 

     this.x = 1; 
    } 

    public int get() { 
     return this.x; 
    } 
} 

Sicuro:

public static class Base { 
    public final void print() { 
     System.out.println(this.get()); 
    } 

    public int get() { 
     return 2; 
    } 
} 

public static final class Sub extends Base { 

    private int x; 

    public Sub() { 
     super(); 
     this.x = 1; 

     this.print(); 
    } 

    public int get() { 
     return this.x; 
    } 
} 

Modifica - Modifica: Dopo ritentare il seguente codice e chiamare getClass(), in realtà non compilare. Quindi ignorare completamente le precedenti dichiarazioni su Object.getClass() che è l'unico metodo disponibile per chiamare prima dell'inizializzazione del genitore. Non ho idea di come sia mancato, e mi dispiace per ogni confusione.

+1

Sì, viene chiamato prima dei costruttori del genitore, tuttavia il metodo ** funzionerà correttamente, perché non si basa sullo stato dell'istanza dell'oggetto (e l'oggetto 'Classe' è già completamente compilato al momento in cui entra nel suo costruttore). Questo è il motivo * esatto * per cui è l'unico metodo che possiamo chiamare prima di una chiamata ai super-costruttori .... – Pacerier

+1

La tua conclusione sta dicendo che l'articolo è ** sbagliato ** perché chiamare 'getClass()' all'interno del costruttore non ci dai mai errori? – Pacerier

+2

-1 - Anche se sono d'accordo con il primo paragrafo di questa risposta, preferisco rigorosamente la scienza al handwaving. Non affrontare direttamente il problema principale ('Will getClass() (...) ci dà mai problemi? (...) quali sono alcuni esempi [che] ci darebbero errori?'), Parafrasando il contenuto della domanda come risposta, usando frasi come "dovresti essere abbastanza sicuro (...) entro la ragione", "probabilmente sicuro, anche se non ci conterei", per non parlare di quest'ultimo "Modifica:" - e tutto ciò senza riferimenti , nessun esempio concreto o prova di qualsiasi cosa. – vaxquis

1
$ javac whose/MyClassLoader.java 
whose/MyClassLoader.java:5: error: cannot reference this before supertype constructor has been called 
     super(getClass().getClassLoader()); 
      ^
1 error 

So che è tardi.

+0

Per qualche ragione, * posso * compilare ed eseguirlo bene su Eclipse. * Strano *, qualche idea perché è così? – Pacerier

+1

Luna dice: Non posso fare riferimento a un metodo di istanza mentre invoco esplicitamente un costruttore \t MyClassLoader.java –

+0

@vaxquis, Puoi trovarlo qui: http://stackoverflow.com/questions/25561873/is-it-always-safe-to -call-getclass-within-the-constructor # comment-39919953 – Pacerier