2010-02-20 21 views
47

Dato che ho una classe Base che ha un costruttore di argomento singolo con un oggetto TextBox come argomento. Se ho una classe semplice della forma seguente:È possibile eseguire il calcolo prima di super() nel costruttore?

public class Simple extends Base { 
    public Simple(){ 
    TextBox t = new TextBox(); 
    super(t); 
    //wouldn't it be nice if I could do things with t down here? 
    } 
} 

otterrò un errore che mi dice che la chiamata a super deve essere la prima chiamata in un costruttore. Tuttavia, stranamente, posso farlo.

public class Simple extends Base { 
    public Simple(){ 
    super(new TextBox()); 
    } 
} 

Perché è consentito, ma il primo esempio non lo è? Posso capire di aver bisogno di impostare prima la sottoclasse, e forse non permettere che le variabili oggetto vengano istanziate prima che venga chiamato il super-costruttore. Ma t è chiaramente una variabile di metodo (locale), quindi perché non permetterlo?

C'è un modo per aggirare questa limitazione? Esiste un modo valido e sicuro per tenere le variabili su cose che potresti costruire PRIMA di chiamare super ma DOPO aver inserito il costruttore? O, più genericamente, permettendo che il calcolo sia fatto prima che venga effettivamente chiamato super, ma all'interno del costruttore?

Grazie.

+2

per quale motivo possibile è questo stato taggato come gwt? Perché lo stavi provando in gwt ?? –

+2

TextBox era una classe GWT, ma no, non è rilevante, suppongo. –

risposta

54

Sì, c'è una soluzione alternativa per il tuo caso semplice. Puoi creare un costruttore privato che prende come argomento TextBox e chiamalo dal tuo costruttore pubblico.

public class Simple extends Base { 
    private Simple(TextBox t) { 
     super(t); 
     // continue doing stuff with t here 
    } 

    public Simple() { 
     this(new TextBox()); 
    } 
} 

Per cose più complicate, è necessario utilizzare una fabbrica o un metodo di fabbrica statico.

+1

Questa sembra essere una buona risposta alla domanda su come usereste i riferimenti agli oggetti creati dopo la costruzione ma prima di super. –

+2

Questo è un modello molto utile, e praticamente ti permette di eludere completamente il requisito che 'super()' non appaia dopo qualsiasi altra cosa nel costruttore. In casi estremi puoi anche mettere insieme più costruttori privati, anche se non riesco a pensare a nessuna situazione in cui ho bisogno di farlo, e se lo facessi probabilmente comincerei a chiedermi se ci fosse un modo migliore per realizzare ciò che io stava facendo. –

+0

Sì. Altri avevano già risposto _why_ funziona così in Java, volevo solo sottolineare una soluzione particolare. Penso che per la maggior parte degli altri casi _valid_ si possa arrivare, è anche possibile trovare una soluzione accettabile. Ora, per contraddire me stesso, c'è un caso in cui questo mi ha morso: stavo estendendo una classe il cui costruttore aveva un parametro di Classe. Volevo chiamare 'super (this.getClass())', ma dato che non ti è permesso fare riferimento a 'this', quel codice era illegale, anche se sembra perfettamente ragionevole che dovrei essere in grado di conoscere la classe attuale in questo punto. – waxwing

0

Il motivo per cui il secondo esempio è consentito ma non il primo è più probabile che mantenga la lingua in ordine e non introduca regole strane.

Consentire l'esecuzione di qualsiasi codice prima che sia stato chiamato super sarebbe pericoloso dato che si potrebbero creare problemi che avrebbero dovuto essere inizializzati ma che non sono ancora stati eseguiti. Fondamentalmente, immagino che tu possa fare un sacco di cose nella chiamata al super stesso (ad esempio, chiama un metodo statico per calcolare alcune cose che devono andare al costruttore), ma non sarai mai in grado di usare nulla dal non -un oggetto completamente costruito che è una buona cosa.

7

È la lingua required per garantire che la superclasse sia costruita in modo affidabile prima. In particolare, "Se un costruttore non richiama esplicitamente un costruttore di superclasse, il compilatore Java inserisce automaticamente una chiamata al costruttore no-argument della superclasse."

Nell'esempio, la superclasse può contare sullo stato di t in fase di costruzione. Puoi sempre chiedere una copia più tardi.

C'è una lunga discussione here e here.

+2

È interessante notare che Java si fa in quattro per impedire uno dei casi più importanti in cui il wrapping del codice attorno a una chiamata del costruttore della superclasse sarebbe utile: assicurando che se un costruttore genera un'eccezione, le cose possono essere ripulite. Ad esempio, se uno dei sovraccarichi del costruttore di un oggetto legge i dati da un file passato, un costruttore di classe derivata potrebbe avere un overload che accetta un nome file, passa il costruttore di una superclasse un file appena aperto e quindi chiude il file dopo. Se il costruttore della superclasse genera, come può il file essere chiuso? – supercat

+0

@supercat: devi progettare per ereditarietà o precluderlo [Bloch, * EffectiveJava *, §17]; guarda anche questo [Q & A] (http://stackoverflow.com/q/1371369/230513). – trashgod

+0

Se la costruzione di un oggetto sottoclasse richiede un modello "acquisisci risorsa, costruisca superclasse, rilascio risorsa", come dovrebbe essere implementato? Passa al costruttore interno un oggetto che implementa un metodo a interfaccia singola da utilizzare in caso di errore? Vagamente praticabile, forse, a spese di incatenare alcuni livelli extra di chiamate al costruttore. – supercat

0

Ecco come funziona Java :-) Ci sono motivi tecnici per cui è stato scelto in questo modo. Potrebbe anche essere strano che non si possano fare calcoli sui locals prima di chiamare super, ma in Java l'oggetto deve prima essere allocato e quindi deve andare fino ad Object in modo che tutti i campi siano correttamente inizializzati prima che si possa modificare accidentalmente loro.

Nel tuo caso c'è il più delle volte un getter che ti permette di accedere al parametro che hai dato a super(). Quindi useresti questo:

 
super(new TextBox()); 
final TextBox box = getWidget(); 
... do your thing... 
+2

Che "tutti i campi siano inizializzati correttamente" non è attualmente garantito in Java (diversamente da IIUC, C++) poiché un costruttore di superclasse può chiamare un metodo di istanza non -finale che una sottoclasse sovrascrive. L'override non vedrà quindi i campi inizializzati dalla sottoclasse (anche quelli finali con gli inizializzatori di istanza!). Alcuni strumenti di analisi statica avvertiranno giustamente di questa situazione. –

17

Ho avuto lo stesso problema con il calcolo prima della super chiamata. A volte si desidera verificare alcune condizioni prima di chiamare super(). Ad esempio, hai una classe che utilizza molte risorse quando viene creata. la sottoclasse desidera alcuni dati aggiuntivi e potrebbe volerli controllare prima di chiamare il super-costruttore. C'è un modo semplice per risolvere questo problema. potrebbe apparire un po 'strano, ma funziona bene:

Utilizzare un metodo statico privato all'interno della vostra classe che restituisce l'argomento del super-costruttore e rendere il vostro controlli all'interno:

public class Simple extends Base { 
    public Simple(){ 
    super(createTextBox()); 
    } 

    private static TextBox createTextBox() { 
    TextBox t = new TextBox(); 
    t.doSomething(); 
    // ... or more 
    return t; 
    } 
} 
Problemi correlati