¿Por qué Swift inicializa los campos propios de la subclase primero?

9

En el lenguaje Swift, para inicializar una instancia, uno debe completar todos los campos de esa clase, y solo luego llamar a superconstructor:

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)
    }
}

Para mí, parece al revés, porque la instancia debe selfcrearse antes de asignar valores a sus campos, y ese código da la impresión de que el encadenamiento ocurre solo después de la asignación. Aparte de eso, la superclase no tiene medios legales para leer los atributos introducidos de su subclase, por lo que la seguridad no cuenta en este caso.

Además, muchos otros lenguajes, como JavaScript, e incluso el Objetivo C, que es un ancestro espiritual de Swift, requieren un encadenamiento antes de acceder self, no después.

¿Cuál es el razonamiento detrás de esta elección para requerir que los campos se definan antes de llamar al superconstructor?

Zomagk
fuente
Interesante. ¿Existe alguna restricción en la que se puedan colocar invocaciones de métodos (virtuales, en particular), como, por ejemplo, después de encadenar? En C #, los campos de una subclase reciben el valor predeterminado, luego encadenamiento / construcción de superclase, luego puede inicializarlos para real, IIRC ..
Erik Eidt
3
El punto es que debe inicializar todos los campos antes de permitir el acceso sin restricciones self.
CodesInChaos
@ErikEidt: en Swift, solo los campos opcionales se inicializan automáticamente a cero.
gnasher729

Respuestas:

9

En C ++, cuando crea un objeto Derivado, comienza como un objeto Base mientras se ejecuta el constructor Base, por lo que en el momento en que se ejecuta el constructor Base, los miembros Derivados ni siquiera existen. Por lo tanto, no necesitan ser inicializados, y no podrían ser inicializados. Solo cuando el constructor Base ha finalizado, el objeto cambia a un objeto Derivado con muchos campos no inicializados que luego se inicializan.

En Swift, cuando crea un objeto derivado, es un objeto derivado desde el principio. Si los métodos se anulan, entonces el método de inicio Base ya usará los métodos anulados, que pueden acceder a las variables miembro Derivadas. Por lo tanto, todas las variables miembro Derivadas deben inicializarse antes de llamar al método Base init.

PD. Mencionaste Objective-C. En Objective-C, todo se inicializa automáticamente a 0 / nil / NO. Pero si ese valor no es el valor correcto para inicializar una variable, entonces el método de inicio Base podría llamar fácilmente a un método que se anula y utiliza la variable aún no inicializada con un valor de 0 en lugar del valor correcto. En Objective-C, eso no es una violación de las reglas del lenguaje (así es como se define que funciona), pero obviamente es un error en su código. En Swift, ese error no está permitido por el idioma.

PD. Hay un comentario "¿es un objeto derivado desde el principio, o no es observable debido a las reglas del lenguaje"? La clase Derivada ha inicializado sus propios miembros antes de que se llame al método Base init, y estos miembros Derivados mantienen sus valores. Entonces, es un objeto Derivado en el momento en que se llama Base init, o el compilador tendría que hacer algo bastante extraño. Y justo después de que el método Base init haya inicializado todos los miembros de la instancia Base, puede invocar funciones anuladas y eso demostrará que es una instancia de la clase derivada.

gnasher729
fuente
Muy sensible. Me tomé la libertad de agregar un ejemplo. Siéntase libre de retroceder si no estaba claro o si entendí mal el punto.
Zomagk
¿Es un objeto derivado desde el principio, o las reglas del lenguaje lo hacen no observable? Creo que es lo último.
Deduplicador
9

Esto proviene de las reglas de seguridad de Swift, como se explica en la sección Inicialización de dos fases en la página de Inicialización del documento de idioma .

Asegura que cada campo esté configurado antes de su uso (especialmente los punteros, para evitar bloqueos).

Swift logra esto con una secuencia de inicialización de dos fases: cada inicializador debe inicializar todos sus campos de instancia, luego llamar a un inicializador de superclase para hacer lo mismo, y solo después de que suceda en el árbol, estos inicializadores pueden dejar selfescapar el puntero, llamar a la instancia métodos, o leer los valores de las propiedades de instancia.

Luego pueden hacer una mayor inicialización, asegurando que el objeto está bien formado. En particular, todos los punteros no opcionales tendrán valores válidos. nil no es válido para ellos.

El objetivo C no es muy diferente, excepto que 0 o nulo siempre es un valor válido, por lo que la inicialización de la primera fase la realiza el asignador simplemente configurando todos los campos a 0. Además, Swift tiene campos inmutables, por lo que deben inicializarse en la fase uno . Y Swift hace cumplir estas reglas de seguridad.

Jerry101
fuente
Por supuesto, sería mucho más difícil con MI.
Deduplicador
3

Considerar

  • Un método virtual definido en la clase base se puede redefinir en la clase derivada.
  • El contratista de la clase base puede llamar a este método virtual directa o indirectamente.
  • El método virtual redefinido (en la clase derivada) puede depender del valor de un campo en la clase derivada que se establece correctamente en el contratista de la clase derivada.
  • El contratista de la clase derivada puede llamar a un método en la clase base que depende de los campos que se hayan establecido en el contratista de la clase base.

Por lo tanto, no existe un diseño simple que haga que el contratista sea seguro cuando se permiten métodos virtuales , Swift evita estos problemas al requerir la inicialización de dos fases, por lo tanto, brinda una mayor seguridad al programador y al mismo tiempo resulta en un lenguaje más complejo.

Si puede resolver estos problemas de una manera agradable, no pase "Ir", proceda directamente a la recolección de su PHd ...

Ian
fuente