为什么在Swift类中使用必需的初始值设定项?
我正在努力理解Swift类中为什么在Swift类中使用必需的初始值设定项?,swift,initialization,Swift,Initialization,我正在努力理解Swift类中required关键字的用法 class SomeClass { required init() { // initializer implementation goes here } } required不会强制我在子类中实现该方法。如果我想重写父类的required指定初始值设定项,我需要编写required,而不是override。我知道它是如何工作的,但不明白我为什么要这样做 必需的有什么好处? 据我所知,像C这样的语言并没
required
关键字的用法
class SomeClass
{
required init() {
// initializer implementation goes here
}
}
required
不会强制我在子类中实现该方法。如果我想重写父类的required
指定初始值设定项,我需要编写required
,而不是override
。我知道它是如何工作的,但不明白我为什么要这样做
必需的
有什么好处?
据我所知,像C这样的语言并没有这样的功能,而且可以很好地使用override
,根据:
所以是的,required强制所有子类实现这个构造函数。然而,这是不必要的
if you can satisfy the requirement with an inherited initializer.
因此,如果您创建了无法用父构造函数完全初始化的更复杂的类,则必须实现require构造函数
文档中的示例(添加了一些内容):
如果您试图在子类中添加您自己的初始化器,那么您必须遵循在超类中声明的某些内容。因此,它确保您不会忘记实现所需的方法。如果您忘记了编译器将给您错误
//致命错误,我们还没有包含必需的init()
。另一个原因是它创建了一组任何子类都应该遵循的条件。子类正在定义自己的初始值设定项。这实际上只是满足编译器的一种方式,以确保如果这个类有任何子类,它们将继承或实现相同的初始值设定项。在这一点上存在疑问,因为如果一个子类有自己指定的初始值设定项,则不会继承来自超类的初始值设定项。因此,一个超类可能有一个初始值设定项,而子类可能没有<代码>必需克服了这种可能性
编译器需要以这种方式满足的一种情况涉及协议,其工作原理如下:
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(名称:)
初始值设定项?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"
正如您在上面的示例中所看到的,我已经在超类上声明了required init()
,init()
超类的初始值设定项默认继承了子类上的子类,因此您可以创建子类let instanceSubClass=subClass()
的实例
但是,假设您想在子类上添加一个指定的初始值设定项,以将运行时值分配给存储的属性neakName
。当然,您可以添加它,但这将导致超类中的初始值设定项不会继承到子类,因此,如果您要创建子类的实例,您将通过其自己指定的初始值设定项创建,如下所示
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"
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"
在上面的这里,您不能仅仅通过子类()
,创建子类的实例,但是如果您想让超类的每个子类都有自己的init()
初始值设定项来创建子类()
的直接实例。只需将required
关键字放在超类上的init()
之前,它将强制您在子类上添加init()
初始值设定项,如下所示
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"
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"
类超类{
变量名称:String
必需的init(){
//初始化器实现在这里
self.name=“无标题”
}
}
类子类:超类{
var neaname:String=“子类未命名”
init(neakame:String){
self.neakame=neakame
}
}//由于继承的原因,不需要在子类中实现编译器错误,这很有意义。然而,我也很想知道这里的好处。如果一个子类(假设覆盖init()
,但不是某个必需的初始值设定项)通过调用继承的必需初始值设定项的进程获得init,并且该必需的初始值设定项随后调用init()
,那么它不会调用该子类的“覆盖的init”,一切正常吗?--我认为唯一好的理由是,所需的初始值设定项不在self上调用init()
,因此将永远不会调用重写“init()”。这在上下文中可能很有趣:。这没有意义。然后,为了解决这个问题,您需要在子类上提供init(),这样子类var就没有设置。这导致了大量无法维护的代码,依赖于var。。。!和运行时错误,如果曾经使用过“required”init而没有返回并清理这些“将在使用”vars之前初始化”,而不是让。。。要求init()调用super.init(withanyargs)可以更好地解决这个问题。所以,如果“noisedog”有“let noise:String”,那么在“required init(name:String)”中您会怎么做?是将var更改为“var noise:String!”的“正确”方法希望如果有人开始使用Dog.Type,他们也知道如何设置“noise”var之后?或者您是否必须提供一些自定义初始值设定项替换的默认值,如“var noise=“Arf!”?我在这里寻找最佳实践(这似乎是一个考虑不周的语言结构,b