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

问题描述 投票:0回答:4

我正在尝试了解

required
关键字在 Swift 类中的使用。

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

required
并不强迫我在我的子类中实现该方法。如果我想重写父类的
required
指定初始化程序,我需要编写
required
而不是
override
。我知道它是如何工作的,但不明白为什么我应该这样做。

required
有什么好处? 据我所知,像 C# 这样的语言没有这样的东西,并且可以很好地使用
override

swift initialization
4个回答
120
投票

这实际上只是满足编译器的一种方式,以确保如果此类有任何子类,它们将继承或实现相同的初始值设定项。关于这一点存在疑问,因为以下规则:如果子类有自己的指定初始化器,则不会继承超类的初始化器。因此,超类可能有一个初始化器,而子类有它。

required
克服了这种可能性。

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

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

问题是,如果 Bird 有一个子类,那么该子类必须实现或继承

init
,而你并不能保证这一点。将 Bird 的
init
标记为
required
确实可以保证这一点。

或者,您可以将 Bird 标记为

final
,从而保证相反的情况,即它永远不会有子类。

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

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 子类上的
init(name:)
初始值设定项。但是编译器如何确定子类将具有
init(name:)
初始值设定项?
required
指定可以平息编译器的恐惧。


14
投票

根据文档

在类初始值设定项的定义之前写入所需的修饰符 指示该类的每个子类都必须实现该初始值设定项。

所以是的,

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

[...] 如果您可以使用继承的初始值设定项满足要求。

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

required
构造函数。

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

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

11
投票

除了上面给出的 Matt 之外,我想提请注意

Required
提供的另一个解决方案。

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"

正如您可以在上面的示例中查看的那样,我在

required init()
上声明了
superClass
,超类的
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()
之前,它就会强制您在
init()
上添加
subClass
初始值设定项 - 如下所示。

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,当您希望所有子类必须已实现超类的

required
时,请在超类的初始化程序之前使用
required initializer
关键字。


0
投票

如果您尝试在子类中添加自己的初始化程序,那么您必须遵循超类中声明的某些内容。因此,它确保您不会忘记实现所需的方法。如果你忘记了,编译器会给你错误

// fatal error, we've not included the required init()
。另一个原因是它创建了一组条件,所有子类都应该遵循它,子类正在定义自己的初始化程序。

© www.soinside.com 2019 - 2024. All rights reserved.