Swift类构造器的继承和重写

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift类构造器的继承和重写相关的知识,希望对你有一定的参考价值。

参考技术A Swift中默认情况下,子类不会自动继承父类的构造器,只有在安全和适当的情况下,子类会自动继承父类的构造器。
安全适当的情况是指:(官方的表述是有错误的),第二条并不是必须子类的属性要有默认值,在实现的构造器中,子类完成自己的属性初始化就行

第一条很好理解,子类没有指定构造器并且自己的属性都有默认值,所以直接调用父类的指定构造器就可以完成初始化,所以会继承

第二条也很好理解,子类实现了父类的所有指定构造器,子类获得的便利构造器,可以保证子类实例初始化,因为,这些便利构造器最终要调用到本类中的指定构造器,因为子类重写了所有父类指定构造器,所有继承来的便利构造器最终在子类中一定可以找到指定构造器完成完全初始化

第一种情况

如果在子类中有和父类一样的构造器,子类构造器必须加上override关键字,因为此时你在重写父类的构造器。

那么编译器如何认为两个构造器是否相同?
即原型相同,原型包含名称和参数表,名称都是init,不用考虑,参数表是指参数个数,参数类型,参数顺序,还有参数标签,只有都相同才认为是相同的构造器

另外需要注意的一点是,子类的构造器和父类的便利构造器相同的时候,不叫重写,不须加override关键字,因为在子类中无法调用父类的便利构造器,所以这不是重写

swift 学习- 14 -- 继承

// 一个类可以继承另一个 类的方法, 属性和其他特征, 当一个类继承其他类时, 继承类叫子类, 被继承类叫父类 或 (超类), 在 Swift 中, 继承是区分 [类] 和其他类型的 一个基本特征

 

// 在 Swift 中, 类可以调用和访问超类的方法, 属性 和 下标, 并且可以重写这些方法, 属性 和 下标来优化或修改他们的行为

// Swift 会检查你的重写定义在超类中是否有匹配的定义, 一次确保你的重写行为是正确的

 

// 可以为类中继承来的属性添加属性观察器, 这样一来, 当属性值改变时, 类就会被通知到, 可以为 任意属性 添加 属性观察器,无论他原本被定义为 存储属性 还是 计算属性

 

 

 

 

// 定义一个基类

// 不继承于其他类的类, 称之为 基类

// 注意 : Swift 中的类并不是 从一个通用的 基类继承而来, 如果你不为你定义的类指定一个 超类 的话, 这个类就自动成为 基类

 

 

// 一个例子定义了一个叫做 Vehicle 的基类, 这个基类声明了一个名为 currentSpeed , 默认值是 0.0 的存储属性 (属性类型推断为 Double), currentSpeed 属性的值被一个 String 类型的只读计算型属性 description 使用, 用来创建车辆的描述 . Vehicle 基类也定义了一个名为 makeNoise 的方法, 这个方法实际上部位 Vehicle 实例做任何事, 但之后将会被 Vehicle 的子类定制

 

class Vehicle{

    var currentSpeed = 0.0

    var description: String {

        return "traveling at \(currentSpeed) miles per hour"

    }

    

    func makeNoise() {

        // 什么也不作

    }

}

 

 

let someVehicle = Vehicle()

print("Vehicle: \(someVehicle.description)")

 

 

 

 

// 子类生成

// 子类生成 指的是在一个已有类的基础上创建一个新的类, 子类继承超类的特性, 并且可以进一步完善, 你还可以为子类添加新的特性

// 为了指明某个类的超类, 将超类名卸载子类名的后面, 用冒号分割:

 

class Bicycle: Vehicle{

    var hasBasket = false

}

 

// 新的 Bicycle 类自动获得 Vehicle 类的所有属性 和 方法, 除了他所继承的特性, Bicycle 类还定义了一个默认值为 false 的存储属性 hasBasket (Bool 型)

 

// 默认情况下,你创建的任何新的 Bicycle 实例将不会有一个篮子,但是可以为特定的 Bicycle 实例设置 hasBasket 属性为 ture

 

let bicycle = Bicycle()

bicycle.hasBasket = true

 

// 还可以修改 Bicycle 实例所继承的 currentSpeed 属性, 和查询实例所继承的 descriotion 属性:

bicycle.currentSpeed = 15.0

print("Bicycle: \(bicycle.currentSpeed)")

 

 

// 子类还可以继续被其它类所继承 下面的示例为Bicycle创建了一个名为Tandem(双人自行车)的子类:

 

 

class Tandem: Bicycle {

    var currentNumberOfPassengers = 0

}

 

 

// Tandem从Bicycle继承了所有的属性与方法,这又使它同时继承了Vehicle的所有属性与方法。Tandem也增加了一个新的叫做currentNumberOfPassengers的存储型属性,默认值为0。

 

 

// 如果你创建了一个Tandem的实例,你可以使用它所有的新属性和继承的属性,还能查询从Vehicle继承来的只读属性description:

 

 

let tandem = Tandem()

tandem.hasBasket = true

tandem.currentNumberOfPassengers = 2

tandem.currentSpeed = 22.0

print("Tandem: \(tandem.description)")

// 打印:"Tandem: traveling at 22.0 miles per hour"

 

 

 

 

// 重写

// 子类可以为继承来的实例方法, 类方法, 实例属性, 或 下标提供自己定制实现, 我们把这种行为叫做 重写

 

// 如果要重写某个特性, 你需要在重写定义的前面加上 override 关键字, 这么做, 你就表明了你是想提供一个重写版本, 而非错误地提供一个相同的定义, 意外的重写可能导致 不可预知的 错误, 任何缺少 override 关键字的 重写都会在编译时诊断为 错误

 

// override 关键字会提醒 Swift 编辑器去检查该类的 超类,是否有匹配重写版本的声明, 这个检查可以确保你的重写定义是正确的

 

 

 

 

// 访问超类的方法, 属性 和 下标

// 当你在子类中重写超类的方法,属性 或 下标时, 有时 在你的重写版本中 使用已经存在的 超类实现 会大有裨益, 比如, 你完全可以完善已有实现的行为, 或在一个继承来的变量中存储一个修改过的值

 

// 在适当的地方, 你可以通过使用 super 前缀来访问超类版本的方法, 属性 或 下标

// 1:  在方法someMethod()的重写实现中,可以通过super.someMethod()来调用超类版本的someMethod()方法。

// 2:  在属性someProperty的 getter 或 setter 的重写实现中,可以通过super.someProperty来访问超类版本的someProperty属性。

// 3:  在下标的重写实现中,可以通过super[someIndex]来访问超类版本中的相同下标。

 

 

 

 

// 重写方法

// 在子类中, 你可以重写继承来的实例方法或类方法, 提供一个定制或替代的方法实现

 

class Train: Vehicle{

    override func makeNoise() {

        print("Choo Choo")

    }

}

 

// 这个例子定义了 Vehicle 的一个新的子类, 叫 Train, 它重写了从 Vehicle 类继承来的 makeNoise() 方法

 

let train = Train()

train.makeNoise()

 

 

 

 

// 重写属性

// 你可以重写继承来的实例属性或类型属性, 提供自己定制的 getter 和 setter,或添加属性观察器 使 重写的属性 也可以观察值什么时候变化

 

 

 

// 重写 属性的 getter 和 setter 

// 你可以提供定制的 getter 和 setter 来重写任意继承来的属性, 无论继承来的属性是存储型的 还是 计算型属性, 子类并不知道继承来的属性是 存储型的 还是 计算型的, 它只知道继承来的属性会有一个 名字和类型 , 你在重写一个属性时, 必须将它的 名字和类型 都写出来, 这样才能使编译器 去检查你重写的属性 是否与 超类中同名同类型的属性 相匹配的

 

// 你可以将一个继承来的 只读属性 重写为 读写属性,只需要在重写版本的属性里 提供 getter 和 setter 即可, 但是,你不能将一个继承来的读写属性 重写为 一个只读属性

 

// 注意 : 如果你在重写属性中提供了 setter ,那么你也一定要提供 getter, 如果你不想在重写版本中的 getter 里修改继承来的属性值, 你可以直接通过 super.someProperty 来返回继承来的值, 其中 someProperty 是你要重写的属性的名字

 

 

class Car: Vehicle{

    var gear = 1

    override var description: String{

        return super.description + " in gear \(gear)"

    }

}

 

// 这个例子定义了一个新类,叫Car,它是Vehicle的子类。这个类引入了一个新的存储型属性叫做gear,默认值为整数1。Car类重写了继承自Vehicle的description属性,提供包含当前档位的自定义描述:

 

// 重写的 description 属性首先要调用 super.description 返回 Vehicle 类的 description 属性 之后, Car 类版本的 description 在末尾增加了自定义内容

 

let car = Car()

car.currentSpeed = 25.0

car.gear = 3

print("Car: \(car.description)")

 

 

 

 

// 重写属性观察器

// 你可以通过重写属性为一个 继承来的 属性 添加 属性观察器, 这样一来, 当继承来的属性值发生变化时, 你就会被通知到, 无论哪个属性原本是如何实现的

 

// 注意 : 你不可以为继承来的常量存储属性 或 继承来的只读计算属性添加 属性观察器, 这些属性的值是不可以被设置的. 所以, 为他们提供 willSet 和 didSet 是不恰当的

// 此外, 你不可以同时提供重写的 setter 和 重写的 属性观察器, 如果你想观察属性值的变化, 并且你已经为那个属性提供了定制的 setter ,那么你在 setter 中就可以观察到任何值的变化了

 

class AutomaticCar: Car{

    override var currentSpeed: Double{

        didSet{

            gear = Int(currentSpeed / 10.0) + 1

        }

    }

}

 

// 当你设置 AutomaticCar 的 currentSprrd 属性, 属性的 didSet 观察器就会自动地设置 gear 属性, 为新的速度选择一个合适的档位, 具体来说, 属性观察器将新的速度值除以 10 ,然后向下取得最接近的整数值, 最后加 1 来得到档位 gear 的值, 

 

let automatic = AutomaticCar()

automatic.currentSpeed = 35.0

print("AutomaticCar: \(automatic.description)")

 

 

 

 

// 防止重写

// 你可以通过把方法,属性 或 下标标记为 final 来防止他们被重写, 只需要在声明关键字前 加上 final 修饰符即可

// 如 : final var , final func, final class func , final subscript

 

// 如果你重写了带有 final 标记的方法, 属性或者下标, 在编译时或报错, 

 

// 在类扩展中的方法, 属性 或 下标也可以在扩展的定义里标记为 fnial

 

// 你可以通过在关键字 class 前添加 final 修饰符 (final class) 来将整个类标记为 final 的, 这样的类是不可被继承的, 试图继承这样的类会导致编译错误

 

以上是关于Swift类构造器的继承和重写的主要内容,如果未能解决你的问题,请参考以下文章

swift学习笔记关于类的继承

swift 元类构造方法

swift 元类构造方法

Swift中可能失败的构造器的传播(调用)和重写

继承与抽象类和接口

swift学习第十三天:类的构造函数