美文网首页
Swift--类继承

Swift--类继承

作者: 优雅的步伐 | 来源:发表于2020-06-23 00:12 被阅读0次
    • 继承概念
    • 构造函数继承
    • 重写
    • 类型检查与转换

    在Swift中,类的继承只能是单继承,而多继承可以通过遵从多个协议实现。也就是说,在Swift中,一个类只能继承一个父类,但是可以遵从多个协议

    构造函数调用规则
    指定构造函数

    类构造函数代理分为横向代理向上代理, 横向代理只能发生在同一类内部,这种构造函数称为便利构造函数。向上代理发生在继承的情况下,在子类构造过程中,要先调用父类构造函数初始化的存储属性,这种构造函数称为指定构造函数

    如下图:

    image.png image.png

    父类Person, 1号和2号 是便利构造函数,3号是指定构造函数,便利构造函数是横向代理,它的特点是必须调用其它的构造函数,1号调2号,2号调3号。

    子类Student, 4号是指定构造函数,5号是便利构造函数,
    4号是调用父类中的构造函数3号,完成对父类构造属性的初始化。父类中的3号就是指定构造函数,4号也是指定构造函数。5号是便利构造函数调用4号构造函数进行初始化。

    所以:指定构造函数一定是父类和子类之间的调用,属于向上代理,便利构造函数一定是横向代理

    构造函数调用规则
    • 指定构造函数必须调用其直接父类的指定构造函数。Student 中的4号指定构造函数调用Person中的3号指定构造函数。

    • 便利构造函数必须调用同一类中定义的其它构造函数。Student中的5号便利构造函数调用同一类中的4号构造函数,Person中的1号便利构造函数调用同一类中的2号构造函数。

    • 便利构造函数必须最终以调用一个指定构造函数结束。Student中的5号便利构造函数调用同一类中的4号指定构造函数,Person中的2号便利构造函数调用同一类中的3号指定构造函数。

    构造过程安全检查

    构造过程的两个阶段, 如下图:

    image.png

    第一阶段,首先分配内存,初始化子类存储属性,沿构造函数链向上初始化父类存储属性,到达构造函数链顶部, 初始化全部的父类存储属性。

    第二阶段,从顶部构造函数链往下,可以对每个类进行进一步修改存储属性、调用实例方法等处理。

    两段式构造过程中,第一阶段构造完成的标志是:调用完父类指定构造函数,即super.init语句;如果没有调用父类构造函数,则是调用它本身便利构造函数,即self.init语句

    安全检查1

    指定构造函数必须保证其所在类的所有存储属性都初始化完成,之后才能向上调用父类构造函数代理。

    class Student: Person {
        var school: String
        init(name: String, age: Int, school: String) {
            self.school = school
            super.init(name: name, age: age)
        }
    
        convenience override init(name: String, age: Int) {
            self.init(name: name, age: age, school: "清华大学")
        }
    }
    

    school属性,它一定要在super.init(name: name, age: age)语句之前调用

    安全检查2

    指定构造函数必须先向上调用父类构造函数代理,然后再为继承的属性设置新值,否则指定构造函数赋予的新值将被父类中的构造函数所覆盖。

    image.png

    :name和age 属性是从父类继承下来的,name和age属性赋值语句要在向上调用父类构造函数代理之后进行。

    安全检查3

    便利构造函数必须先调用同一类中的其他构造函数代理,然后再为任意属性赋新值,否则便利构造函数赋予的新值将被同一类中的其它指定构造函数覆盖。

    image.png

    :便利构造函数init(name: String, age: Int)调用同一类中的其它构造函数代理“ self.init(name: name, age: age, school: "清华大学")”,然后再调用self.name = "Tom"语句为name属性赋值。

    安全检查4

    构造函数在第一阶段构造完成之前不能调用实例方法,也不能读取实例属性,因为这时还不能保证要访问的实例属性已经被初始化。

    image.png

    : 读取name属性“print(self.name)”,和调用实例方法“self.toString()” 只能放在“super.init(name: name, age: age)”方法之后,不能放在它之前。

    构造函数继承

    • 条件1: 如果子类没有定义任何指定构造函数,它将自动继承父类的所有指定构造函数。

    • 条件2: 如果子类提供了所有父类指定构造函数的实现,无论是通过条件1继承过来的,还是通过自己编写实现的,它都将自动继承父类的所有便利构造函数。

    如下图:(虚线表示继承过来的构造函数

    image.png

    class Graducate: Student {
        var special: String = ""
    }
    
    let student1 = Student()
    let student2 = Student(name: "Tom")
    let student3 = Student(name: "Tom", age: 30)
    let student4 = Student(name: "Ben", age: 20, school: "哈佛大学")
    

    重写(override)

    • 重写实例属性

    在子类中重写从父类继承来的属性,属性的重写一方面可以重写gettersetter访问函数,另一方面可以重写属性观察者

    重写getter和setter访问函数

    class Student: Person {
        var school: String
    
        override var age: Int {
            get {
                return super.age
            }
            set {
                super.age = newValue < 8 ? 8: newValue
            }
        }
        convenience init() {
            self.init(name: "Tony", age: 18, school: "清华大学")
        }
    
        init(name: String, age: Int, school: String) {
            self.school = school
            super.init(name: name, age: age)
        }
    }
    
    let student1 = Student()
    print("学生年龄:\(student1.age)")
    student1.age = 6
    print("学生年龄:\(student1.age)")
    

    重写属性观察者 (willSet, didSet)

    class Student: Person {
        var school: String
        //重写属性观察者
        override var age: Int {
            willSet {
                print("学生年龄新值:\(newValue)")
            }    
            didSet {
                print("学生年龄旧值:\(oldValue)")
            }
        }
    
        convenience init() {
            self.init(name: "Tony", age: 18, school: "清华大学")
        }
    
        init(name: String, age: Int, school: String) {
            self.school = school
            super.init(name: name, age: age)
        }
    }
    
    let student1 = Student()
    print("学生年龄:\(student1.age)")
    student1.age = 6
    print("学生年龄:\(student1.age)")
    

    输出结果


    image.png
    • 重写静态属性

    在类中静态属性定义使用class或static关键字,但是使用哪一个要看子类中是否重写该属性。class修饰的属性可以被重写,static修饰的就不能

    class Account {
        var amount: Double = 0.0 //账户金额
        var ower: String = ""    //账户名
    
        var interestRate: Double = 0.0668 //利率
    
        //class不能换成static
        class var staticProp: Double {
            return 0.0668 * 1_000_000
        }
    
        var instanceProp: Double {
            return self.interestRate * self.amount
        }
    }
    
    class TermAccount: Account {
        //class 可 换成static
        override class var staticProp: Double {
            return 0.0700 * 1_000_000
        }
    }
    
    //访问类型属性
    print(Account.staticProp)
    print(TermAccount.staticProp)
    

    应该使用class还是static, 无论使用哪个对它本身的类没有影响, 但是对子类有影响,如果子类中重写了某个属性,父类中应该用class, 不能使用static,如果父类中用了static, 那子类中就不能重写属性了,因为static定义的属性被认为是final, final的意思是不能被修改,不能被重写。

    • 重写实例方法
    image.png
    • 重写静态方法

    与类的静态属性定义类似,静态方法使用class或static关键字,但是使用哪一个要看子类中是否重写该方法。class修饰的静态方法可以被重写,static关键字的就不能

    class Account {
        var amount: Double = 0.0 //账户金额
        var ower: String = ""    //账户名
    
        var interestRate: Double = 0.0668 //利率
    
        //静态属性
        class var staticProp: Double {
           return 0.0668 * 1_000_000
        }
    
        var instanceProp: Double {
            return self.interestRate * self.amount
        }
    
        //静态方法
        class func interestBy(amount: Double)-> Double {
            return 0.08886 * amount
        }
    }
    
    class TermAccount: Account {
        //重写重写静态属性
        override class var staticProp: Double {
            return 0.0700 * 1_000_000
        }
    
        //重写静态方法
        override class func interestBy(amount: Double)-> Double {
            return 0.09 * amount
        }
    }
    
    • 使用final关键字

    我们可以在类的定义中使用final关键字声明类 、属性、方法和下标。final声明的类不能被继承,final声明的属性、方法和下标不能被重写

    如下图:

    截屏2020-06-15 下午11.41.20.png

    类型检查与转换

    使用is进行类型检查

    is操作符可以判断一个实例是否是某个类的类型。如果实例是目标类型,结果返回true,否则为false。

    class Person {
        var name: String
        var age: Int
    
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    
    class Student: Person {
        var school: String = ""
    
        init(name: String, age: Int, school: String) {
            self.school = school
            super.init(name: name, age: age)
        }
    
    }
    
    class Worker: Person {
        var factory: String
    
        init(name: String, age: Int, factory: String) {
            self.factory = factory
            super.init(name: name, age:age)
        }
    }
    
    let student1 = Student(name: "Tom", age: 18, school: "清华大学")
    let student2 = Student(name: "Lucy", age: 18, school: "北京大学")
    let student3 = Student(name: "Lily", age: 19, school: "香港大学")
    
    let worker1 = Worker(name: "Tony", age: 28, factory: "互联网")
    let worker2 = Worker(name: "Ben", age: 29, factory: "电子科技公司")
    
    let people = [student1, student2, student3, worker1, worker2]
    
    print(type(of: people)) //打印people的数据类型
    
    var studentCount = 0
    var workerCount = 0
    
    //判断实例item 是 类Worker类型还是 类Student类型
    for item in people {
        if item is Worker {
            workerCount+=1
        }else if item is Student {
            studentCount+=1
        }
    }
    
    print("工人人数:\(workerCount), 学生人数:\(studentCount)。")
    
    使用as、as!和as?进行类型转换

    as!一般用于可选类型的转换,最后的结果,它会对可选类型进行一次强制的拆封。如果有些情况不支持类型转换的话,强制拆封的结果就会出现异常。

    as?也是可选类型的转换,但是它并不对可选类型的结果进行拆包,特别是当我们不能转换成功的情况下,并不会出现错误,而是将一个空返回给它的调用者。

    let p1: Person = Student(name: "Tom", age: 20, school: "清华大学")//p1: 类型是Person类型,实例是Student实例
    let p2: Person = Worker(name: "Tom", age: 18, factory: "钢厂")//p2: 类型是Person类型,实例是Worker实例
    let p3: Person = Person(name: "Tom", age: 28)//p3: 类型是Person类型,实例是Person实例
    let p4: Student = Student(name: "Ben", age: 40, school: "清华大学")//p4: 类型是Student类型,实例是Student实例
    let p5: Worker = Worker(name: "Tony", age: 28, factory: "钢厂")//p5: 类型是Worker类型,实例是Worker实例
    
    image.png

    如上图:在类型转换时p1-p5可转换为目标类型Person, Worker, Student这三种类型。

    p1: 类型是Person类型,实例是Student实例
    则它可以转化为Person类型,Student类型(向下转型),不能转化为Worker类型。

    p2: 类型是Person类型,实例是Worker实例
    则它可以转化为Person类型和Worker类型(向下转型),不可以转化为Student类型。

    p3: 类型是Person类型,实例是Person实例
    则它可以转化为Person类型, 不可以转化为Worker类型和Student类型。

    p4: 类型是Student类型,实例是Student实例
    则它可以转化为Person类型(向上转型)和Student类型,不可以转化为Worker类型

    p5: 类型是Worker类型,实例是Worker实例
    则它可以转化为Person类型(向上转型)和Worker类型,不可以转化为Student类型

    :多数情况下都是向下转型,向上转型用的很少,能否转换成功要看实例化的实例是否是目标转化类型的实例。

    1、as操作符

    as操作符仅仅应用于向上转型,由于向上转型很少用,所以在代码中很少能够看到使用as操作符的情况。它经常用于非可选类型之间的向上转换。

    let p41: Person = p4 as Person
    //let p41: Person = p4   //也可省略 as
    print(p41)
    
    2、as!操作符

    使用as!操作符可以应用于如下三种情况:将非可选类型转换为非可选类型、将可选类型转换为非可选类型、将可选类型转换为可选类型。示例代码如下:

    //向下转型 使用as!
    //1、将非可选类型转换为非可选类型
    let p11 = p1 as! Student
    // let p111 = p2  as! Student //异常
    
    ///2、将可选类型转换为非可选类型
    let p6: Person? = Student(name: "Tom", age: 23, school: "清华大学")
    //let p6: Person? = nil
    print(p6)
    
    //let p12 = p6 as! Student
    //print(p12)
    
    ///3、将可选类型转换为可选类型
    let p13 = p6 as! Student?
    print(p13)
    
    3、as? 操作符

    as?操作符暗示对类型进行转换之后,结果是可选类型
    使用as?操作符可以应用于如下两种情况:将非可选类型转换为可选类型、将可选类型转换为可选类型。

    //向下转型  as?操作符
    //1、将非可选类型转换为可选类型
    let p21 = p1 as? Student
    print(p21)
    
    let p211 = p2 as? Student  //nil
    
    //2、将可选类型转换为可选类型
    let p7: Person? = Student(name: "Tom", age: 20, school: "清华大学")
    let p22 = p7 as? Student
    print(p22)
    
    使用Any和AnyObject类型

    在Swift中还提供了两种类型表示不确定类型:AnyAnyObject。AnyObject可以表示任何类的类型,而Any可以表示任何类型,包括类和其他数据类型,也包括Int和Double等基本数据类型,当然也包括AnyObject类型。

    class Person {
        var name: String
        var age: Int
    
    
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    
    class Student: Person {
        var school: String = ""
    
        init(name: String, age: Int, school: String) {
            self.school = school
            super.init(name: name, age: age)
        }
    
    }
    
    class Worker: Person {
        var factory: String
    
        init(name: String, age: Int, factory: String) {
            self.factory = factory
            super.init(name: name, age:age)
        }
    }
    
    let student1 = Student(name: "Tom", age: 18, school: "清华大学")
    let student2 = Student(name: "Lucy", age: 18, school: "北京大学")
    let student3 = Student(name: "Lily", age: 19, school: "香港大学")
    
    let worker1 = Worker(name: "Tony", age: 28, factory: "互联网")
    let worker2 = Worker(name: "Ben", age: 29, factory: "电子科技公司")
    
      let people1: [Person] = [student1, student2, student3, worker1, worker2]
    
    let people2: [AnyObject] = [student1, student2, student3, worker1, worker2]
    
    let people3: [Any] = [student1, student2, student3, worker1, worker2]
    
    //print(type(of: people)) //打印people的数据类型
    
    for item in people3 {
        if let student = item as? Student {
            print("Student school:\(student.school)")
        }else if let worker = item as? Worker {
            print("Worker factory: \(worker.factory)")
        }
    }
    

    相关文章

      网友评论

          本文标题:Swift--类继承

          本文链接:https://www.haomeiwen.com/subject/xgyotktx.html