为什么在 Swift 类 中使用必需的初始化器?

Why use required Initializers in Swift classes?

我试图理解 Swift classes 中 required 关键字的用法。

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

required 并没有强迫我在 child-class 中实现该方法。如果我想覆盖 parent class 的 required 指定初始值设定项,我需要写 required 而不是 override。我知道它是如何工作的,但不明白我为什么要这样做。

required有什么好处? 据我所知,像 C# 这样的语言没有这样的东西,并且与 override.

一起工作得很好

根据 documentation:

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

所以是的,required 确实强制所有子 类 实现此构造函数。然而,这不是必需的

 if you can satisfy the requirement with an inherited initializer.

因此,如果您创建了更复杂的 类 且无法使用父构造函数完全初始化,则必须实现 require 构造函数。

文档中的示例(添加了一些内容):

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

如果您尝试在子 class 中添加您自己的初始化程序,那么您必须遵循在超级 class 中声明的某些内容。所以它确保您不会忘记实现所需的方法。如果你忘记编译器会给你错误// fatal error, we've not included the required init() 。另一个原因是它创建了一组条件,sub class 应该遵循它 sub class 正在定义自己的初始化程序。

这实际上只是一种满足编译器的方式,以确保如果这个 class 有任何子 class,它们将继承或实现相同的初始值设定项。在这一点上有疑问,因为规则如果子class有自己的指定初始化器,则不会继承来自超级class的初始化器 .因此 superclass 有可能有一个初始值设定项而 subclass not 有它。 required 克服了这种可能性。

这种方式需要编译器满足的一种情况涉及到协议,工作方式如下:

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

问题是,如果 Bird 有一个子class,那个子class 将必须实现或继承init,而你不能保证这一点。将 Bird 的 init 标记为 required 确实可以保证。

或者,您可以将 Bird 标记为 final,从而保证相反,即它永远不会有子 class.

另一种情况是你有一个工厂方法可以通过调用相同的初始化器来生成 class 或其子class:

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 正在 Dog 或 Dog subclass 上调用 init(name:) 初始值设定项。但是编译器如何确定 subclass 将具有 init(name:) 初始化程序? required 名称消除了编译器的恐惧。

我想提请注意 Required 提供的另一个解决方案,除了上面给出的 Matt 之外。

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"

正如你在上面的例子中看到的那样,我在 superClass 上声明了 required init()init() 超类的初始值设定项在 subClass 上默认继承,所以你能够创建子类 let instanceSubClass = subClass() 的实例。

但是,假设您想在子类上添加一个指定的初始化器,以将 运行 时间值分配给存储的 属性 neakName。当然你可以添加它,但这会导致 超类的初始化器不会被继承到子类 ,所以如果你要创建 subClass 的实例,你将通过它自己指定的初始化程序如下。

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"

在上面,您将无法仅通过 subClass() 创建 subClass 的实例,但是如果您希望 superClass 的每个子类都必须有自己的实例init() 初始化器subClass() 创建直接实例。只需将 required 关键字放在超类的 init() 之前,它会强制您在 subClass 上也添加 init() 初始值设定项 - 如下所示。

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"  

所以,在超类的初始化器之前使用 required 关键字,当您希望所有子类必须已经实现超类的 required initializer 时。