Perché Swift inizializza prima i campi propri della sottoclasse?


9

Nel linguaggio Swift, per inizializzare un'istanza, è necessario compilare tutti i campi di quella classe e solo allora chiamare supercostruttore:

class Base {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Derived: Base {
    var number: Int

    init(name: String, number: Int) {
        // won't compile if interchange lines
        self.number = number
        super.init(name)
    }
}

Per me sembra al contrario, perché l'istanza deve selfessere creata prima di assegnare valori ai suoi campi e quel codice dà l'impressione come se il concatenamento avvenga solo dopo l'assegnazione. A parte questo, la superclasse non ha mezzi legali per leggere gli attributi introdotti della sua sottoclasse, quindi la sicurezza non conta in questo caso.

Inoltre, molte altre lingue, come JavaScript, e persino l'Obiettivo C, che è in qualche modo un antenato spirituale di Swift, richiedono il concatenamento delle chiamate prima di accedere self, non dopo.

Qual è il ragionamento alla base di questa scelta per richiedere che i campi siano definiti prima di chiamare il supercostruttore?


Interessante. Esiste una restrizione in cui le invocazioni di metodi (virtuali, in particolare) possono essere poste, come, diciamo, solo dopo il concatenamento? In C #, ai campi di una sottoclasse viene assegnato il valore predefinito, quindi la costruzione concatenata / superclasse, quindi è possibile inizializzarli per il IIRC reale.
Erik Eidt,

3
Il punto è che è necessario inizializzare tutti i campi prima di consentire l'accesso illimitato a self.
Codici A Caos il

@ErikEidt: in Swift, solo i campi opzionali vengono automaticamente inizializzati a zero.
gnasher729,

Risposte:


9

In C ++, quando si crea un oggetto Derivato, questo inizia come oggetto Base mentre il costruttore Base è in esecuzione, quindi al momento dell'esecuzione del costruttore Base, i membri Derivati ​​non esistono nemmeno. Quindi non devono essere inizializzati e non possono essere inizializzati. Solo quando il costruttore Base ha terminato l'oggetto viene cambiato in un oggetto Derivato con molti campi non inizializzati che si inizializza.

In Swift, quando si crea un oggetto Derivato, è un oggetto Derivato dall'inizio. Se i metodi vengono sovrascritti, il metodo init Base utilizzerà già i metodi sostituiti, che potrebbero accedere alle variabili dei membri derivati. Pertanto, tutte le variabili membro derivate devono essere inizializzate prima di chiamare il metodo Init init.

PS. Hai menzionato Objective-C. In Objective-C, tutto viene automaticamente inizializzato su 0 / zero / NO. Ma se quel valore non è il valore corretto per inizializzare una variabile, allora il metodo di base Init potrebbe facilmente chiamare un metodo che viene sovrascritto e usa la variabile non ancora inizializzata con un valore di 0 anziché il valore corretto. In Objective-C, questa non è una violazione delle regole del linguaggio (è così che funziona come definito) ma ovviamente un bug nel tuo codice. In Swift, quel bug non è consentito dalla lingua.

PS. C'è un commento "è un oggetto derivato dall'inizio o non è osservabile a causa delle regole del linguaggio"? La classe Derived ha inizializzato i propri membri prima che venga chiamato il metodo init di Base e questi membri Derivati ​​mantengono i loro valori. Quindi o è un oggetto Derivato nel momento in cui viene chiamato Base Init, oppure il compilatore dovrebbe fare qualcosa di piuttosto bizzarro. E subito dopo che il metodo Init di base ha inizializzato tutti i membri dell'istanza di Base, può chiamare funzioni sostituite e ciò dimostrerà che si tratta di un'istanza della classe derivata.


Molto sensibile. Ho preso la libertà di aggiungere un esempio. Sentiti libero di tornare indietro se non ero chiaro o fraintendevo il punto.
Zomagk,

È un oggetto derivato dall'inizio o le regole del linguaggio lo rendono non osservabile? Penso che sia quest'ultimo.
Deduplicatore

9

Ciò deriva dalle regole di sicurezza di Swift, come spiegato nella sezione Inizializzazione in due fasi nella pagina Inizializzazione del documento linguistico .

Assicura che ogni campo sia impostato prima dell'uso (specialmente puntatori, per evitare arresti anomali).

Swift ottiene questo risultato con una sequenza di inizializzazione in due fasi: ogni inizializzatore deve inizializzare tutti i suoi campi di istanza, quindi chiamare un inizializzatore di superclasse per fare lo stesso, e solo dopo ciò accade sull'albero questi inizializzatori possono lasciare selfscappare il puntatore, chiamare istanza metodi o leggi i valori delle proprietà dell'istanza.

Possono quindi effettuare ulteriori inizializzazioni, assicurando che l'oggetto è ben formato. In particolare, tutti i puntatori non opzionali avranno valori validi. zero non è valido per loro.

L'obiettivo C non è molto diverso, tranne per il fatto che 0 o zero è sempre un valore valido, quindi l'inizializzazione della prima fase viene eseguita dall'allocatore impostando tutti i campi su 0. Inoltre, Swift ha campi immutabili, quindi devono essere inizializzati nella fase uno . E Swift applica queste regole di sicurezza.


Certo, sarebbe molto più difficile con l'MI.
Deduplicatore,

3

Prendere in considerazione

  • Un metodo virtuale definito nella classe base può essere ridefinito nella classe derivata.
  • Il contraente della classe base può chiamare questo metodo virtuale direttamente o indirettamente.
  • Il metodo virtuale ridefinito (nella classe derivata) può dipendere dal valore di un campo nella classe derivata impostato correttamente nel contraente della classe derivata.
  • Il contraente della classe derivata può chiamare un metodo nella classe base che dipende dai campi impostati nel contraente della classe base.

Pertanto, non esiste un design semplice che renda sicuro l'appaltatore quando sono consentiti metodi virtuali , Swift evita questi problemi richiedendo l'inizializzazione in due fasi, offrendo quindi una migliore sicurezza al programmatore, ottenendo al contempo un linguaggio più complesso.

Se riesci a risolvere questi problemi in modo piacevole, non passare "Go", procedi direttamente alla raccolta del tuo PHd ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.