web-dev-qa-db-fra.com

Pourquoi utiliser les initialiseurs requis dans les classes Swift?

J'essaie de comprendre l'utilisation du mot clé required dans les classes Swift.

class SomeClass 
{
    required init() {
        // initializer implementation goes here
    }
}

required ne me force pas à implémenter la méthode dans ma classe-enfant. Si je veux remplacer l'initialiseur désigné par required de ma classe parente, je dois écrire required et non override. Je sais comment cela fonctionne mais ne peux pas comprendre pourquoi je devrais le faire.

Quel est l'avantage de required? Autant que je sache, les langages comme C # n'ont pas ce genre de chose et fonctionnent parfaitement avec override.

43
TalkingCode

En fait, c'est juste un moyen de satisfaire le compilateur pour lui assurer que si cette classe avait des sous-classes, elle hériterait ou implémenterait le même initialiseur. Il existe un doute sur ce point, en raison de la règle selon laquelle si une sous-classe a son propre initialiseur désigné, aucun initialiseur de la super-classe n'est hérité . Ainsi, il est possible pour une super-classe d'avoir un initialiseur et la sous-classe pas de l'avoir. required surmonte cette possibilité.

Une situation dans laquelle le compilateur doit être satisfait de cette manière implique des protocoles et fonctionne comme suit:

protocol Flier {
    init()
}
class Bird: Flier {
    init() {} // compile error
}

Le problème est que si Bird avait une sous-classe, cette sous-classe devrait implémenter ou hériter de init, et vous ne l'avez pas garanti. Marquer la variable init comme required le garantit.

Alternativement, vous pouvez marquer Bird comme final, garantissant ainsi l'inverse, à savoir qu'il n'aura jamais de sous-classe.

Une autre situation est celle où vous avez une méthode de fabrique qui peut créer une classe ou sa sous-classe en appelant le même initialiseur:

class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
    let d = whattype.init(name: "Fido") // compile error
    return d
}

dogMakerAndNamer appelle l'initialiseur init(name:) sur Dog ou une sous-classe Dog. Mais comment le compilateur peut-il être sûr qu'une sous-classe aura un initialiseur init(name:)? La désignation required apaise les craintes du compilateur.

73
matt

Je souhaite attirer l’attention sur une autre solution proposée par Required, mis à part ce que Matt a donné ci-dessus. 

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"

}
let instanceSubClass = subClass()
instanceSubClass.name        //output: "Untitled"
instanceSubClass.neakName    //output: "Subclass Untitled"

Comme vous pouvez le vérifier dans l'exemple ci-dessus, j'ai déclaré required init() sur superClass, init() l'initialiseur de superClass a hérité par défaut sur subClass, vous pouvez donc créer une instance de subClass let instanceSubClass = subClass()

Mais supposons que vous souhaitiez ajouter un initialiseur désigné sur la sous-classe pour affecter la valeur d'exécution à la propriété stockée neakName. Bien sûr, vous pouvez l'ajouter, mais cela résultera en aucun initialiseur de la superClass ne sera hérité de subClass. Donc, si vous créez une instance de subClass, vous créerez via son propre initialiseur désigné comme ci-dessous. 

class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"

Ici ci-dessus, vous ne pourrez pas créer une instance de subClass uniquement avec subClass(), Mais si vous souhaitez que chaque sous-classe de superClass doit avoir son propre initialisateur init()pour créer une instance directe par subClass(). Il suffit de placer le mot clé required avant init() sur superClass, ce qui vous obligera à ajouter init() initializer sur subClass - comme ci-dessous. 

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  

SO, utilisez le mot clé required avant initializer sur la superclasse, lorsque vous souhaitez que toutes les sous-classes doivent avoir été implémentées required initializer de la superclasse.

6
Kiran Jasvanee

Selon la documentation :

Write the required modifier before the definition of a class initializer to
indicate that every subclass of the class must implement that initializer

Alors oui, obligatoire force toutes les classes enfants à implémenter ce constructeur. Cependant, ce n'est pas nécessaire

 if you can satisfy the requirement with an inherited initializer.

Ainsi, si vous avez créé des classes plus complexes qui ne peuvent pas être complètement initialisées avec un constructeur parent, vous devez implémenter le constructeur require.

Exemple de documentation (avec quelques ajouts):

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    let thisNeedsToBeInitialized: String
    required init() {
        // subclass implementation of the required initializer goes here
        self.thisNeedsToBeInitialized = "default value"
    }
}
3
pseudonym117

Si vous essayez d'ajouter votre propre initialiseur dans la sous-classe, vous devez alors suivre certaines choses qui ont été déclarées dans la super-classe. Donc, assurez-vous que vous n'oublierez pas d'implémenter cette méthode requise. Si vous oubliez le compilateur, l'erreur // fatal error, we've not included the required init() sera générée. Une autre raison est qu’elle crée un ensemble de conditions que toute sous-classe doit suivre, la sous-classe définissant son propre initialiseur.

0
Amit89