Perché la chiamata di un costruttore al supercostruttore deve essere la prima chiamata?

-2

È un errore se fai qualcosa in un costruttore prima di chiamare il supercostruttore. Ricordo che ho avuto problemi a causa di ciò. Eppure, non vedo come questo ci salvi dagli errori. Potrebbe salvarti dall'utilizzo di campi non inizializzati. Ma il compilatore Java che controlla l'utilizzo di variabili non abilitate lo fa e questa stupida regola non migliora nulla qui.

L'unica considerazione seria che ricordo era che ne abbiamo bisogno per OOP, perché gli oggetti nella vita reale sono costruiti in questo modo: si crea un cetriolo creando prima un ortaggio e poi aggiungendo gli attributi del cetriolo. IMO, è l'opposto. Per prima cosa crei un cetriolo e, per principio anatra è un'anatra , diventa un vegetale. Un altro argomento è che migliora la sicurezza. Non scrivere il codice migliora l'affidabilità molto meglio, quindi non lo considero un argomento. La creazione di codice più complesso quando è necessario risolvere una restrizione stupida, è ciò che rende il programma più soggetto a errori.

Questa regola crea un strong dolore (almeno per me). Quindi, voglio ascoltare la discussione seria e un esempio serio in cui potrebbe essere utile.

    
posta Val 31.07.2013 - 13:37
fonte

4 risposte

9

La sequenza di inizializzazione dell'oggetto è abbastanza complessa così com'è e già a volte provoca mal di testa . Consentire alle sottoclassi di eseguire il codice prima che il costruttore della superclasse lo rendesse più complesso e confuso ancora, con un maggior potenziale di bug sottili, specialmente se esiste una gerarchia di più classi che fanno tutto questo.

Riduce anche l'accoppiamento concettuale tra superclassi e sottoclassi. Osservi la superclasse e puoi vedere come verrà inizializzata prima di essere usata. Permettendo codice subclass per fare roba arbitraria prima che l'inizializzazione della superclasse possa facilmente infrangere alcune ipotesi formulate dal codice della superclasse, e le sottoclassi dovrebbero, per quanto possibile, non dover dipendere dalla comprensione dei dettagli di implementazione della superclasse.

Tutto sommato, non permettendo l'esecuzione del codice di sottoclasse prima che la superclasse sia completamente inizializzata porta a un codice che è più facile da capire e meno soggetto a errori. A volte può richiedere soluzioni alternative, ma solo perché una regola ti impedisce di fare ciò che sarebbe conveniente nelle tue circostanze immediate non significa che sia una regola cattiva o stupida.

    
risposta data 31.07.2013 - 14:39
fonte
4

Quando erediti da un oggetto, potresti non essere in grado di vedere l'implementazione del costruttore o dei metodi sottostanti.

Potrebbero esserci altre cose in corso nel costruttore rispetto alla semplice inizializzazione delle variabili. come l'accesso all'hardware, l'accesso al database, la comunicazione di rete, ecc. Non dire che questo sia un buon design o meno - solo dicendo che potresti non esserne a conoscenza.

Ma anche se era solo variabili. Potrebbero esserci funzioni membro che si basano su tali variabili che vengono istanziate correttamente. Potrebbero esserci chiamate di funzione che si verificano nell'istanziazione di tali variabili. A causa dell'incapsulamento non puoi essere sicuro che non sia così, quindi per sicurezza, costruisci il tuo articolo base per intero, altrimenti l'implementazione potrebbe causare la "rottura" della classe super.

E funziona al contrario. Se qualcuno eredita dalla tua classe, sai che il tuo codice di costruzione verrà eseguito correttamente. Il programmatore che eredita la tua classe non può mai violare il tuo codice, solo il suo. Se ha un problema, sa che dovrebbe controllare il suo codice.

    
risposta data 31.07.2013 - 14:33
fonte
3

You first create a cucumber and, by duck is a duck principle, it becomes a vegetable.

Tranne che Java non è un linguaggio tipizzato strutturalmente (anatra). È una lingua nominalmente digitata.

Se Java era un linguaggio strutturato tipicamente (o semplicemente uno dinamico), allora la tua immagine mentale di come funziona sarebbe buona.

Dato che non lo è, ha bisogno di costruire il layout dell'oggetto in memoria che è fatto in modo più efficiente sovrapponendo i tipi derivati ai tipi di base (si pensi alla cipolla di Shrek) in modo che il layout del tipo di base possa essere riutilizzato solo per il tipo base così come i suoi tipi derivati.

Deve essere così? Niente affatto, e spesso non è per le lingue che non sono nominativamente digitate (o possono avere più tipi di base). Ma in Java lo è, e francamente è una delle restrizioni meno onerose che la lingua ti mette.

Per quanto riguarda la tua domanda, stai ricevendo il pushback perché ti viene fuori come un sapientone che chiaramente non lo fa. È bello chiedersi perché, ma dire che questo design è completamente stupido perché causa problemi (perché non si può fare bene il design orientato agli oggetti) è ignorante al meglio.

E sì, se ti trovi in uno scenario in cui vuoi eseguire operazioni nel costruttore prima di chiamare il costruttore di base è un'indicazione abbastanza solida che non sei così bravo in OOD.

    
risposta data 31.07.2013 - 15:32
fonte
1

In parole semplici:

Una relazione di ereditarietà è una "IS A" relazione.

Se Human eredita da Ape , significa che un Human è, in primo luogo, un Ape quindi un Human . (Non sto dicendo che gli umani discendono dalle scimmie, ma sono delle scimmie).

È logico che tu abbia inizialmente inizializzato il tuo Ape self per crearlo e diventare il tuo Human self.

La superclasse è la base su cui hai costruito, il che significa che devi avere un'istanza della supeclass da cui partire.

Non ha senso costruire la seconda storia della tua casa prima di costruire la prima storia.

    
risposta data 31.07.2013 - 15:09
fonte