类继承

作者: 随心_追梦 | 来源:发表于2017-12-24 23:13 被阅读0次

继承是面向对象的重要特征之一。swift中的继承只能发生在类上,结构体和枚举不能继承。在swift中,一个类继承另一个类的方法、属性、下标等特征。子类继承父类后,可以重写父类的方法、属性、下标等特征。
一般情况下,一个子类只能继承一个父类,称为单继承。但是有的情况下,一个子类可以有多个不同的父类。这称之为多重继承。在swift中,类的继承是单继承,多重继承可以通过遵从多个不同的协议实现。

构造函数继承

类构造函数分为横向代理和向上代理

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

构造函数调用规则

构造函数之间的调用形成了构造函数链,swift限制构造函数之间代理调用的规则有三条:

  1. 指定构造函数必须调用其直接父类的指定构造函数
  2. 便利构造函数必须调用同一类中定义的其他构造函数
  3. 便利构造函数必须最终以调用一个指定构造函数结束
构造过程安全检查

在swift中,类的构造过程包含两个阶段

  • 第一阶段,首先分配内存,初始化子类存储属性,沿构造函数链向上初始化父类存储属性。到达构造函数链顶部,初始化全部的父类存储属性。
  • 第二阶段,从顶部构造函数链往下,可以对每个类进行进一步修改存储属性、调用实例方法等处理。

swift编译器在构造过程中可以进行一些安全检查工作,这些工作可以有效的防止属性在初始化之前被访问,也可以防止属性被另一个构造函数意外的赋予不同的值。例如下面代码中,1和2不能互换,这是因为只有在子类所有的存储属性初始化之后,才能调用父类构造函数初始化父类存储属性。

class Rectangle {
    var width: Double
    var height: Double
    
    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }
}

class SubRectange: Rectangle {
    var name: String
    init(name: String) {
        self.name = name                                 1
        super.init(width: 1.0, height: 2.0)              2
    }
}

为了构造过程顺利进行,swift提供的4中安全检查

  1. 指定构造函数必须保证其所在类的所有存储属性都完成初始化,之后才能调用父类构造函数代理。
  2. 指定构造函数必须先向上调用父类构造函数代理,然后再为继承的属性设置新值。否则指定构造函数赋予的新将被父类中的构造函数所覆盖。
  3. 便利构造函数必须先调用同一类中的其他构造函数代理,然后再为任意属性赋新值,否则便利构造函数赋予的新值将被同一类中的其他指定构造函数覆盖
  4. 构造函数在第一阶段构造完成之前不能调用实例方法,也不能读取实例属性,因为这时还不能保证要访问的实例属性已经被初始化。

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

构造函数继承

swift中的构造函数来源有两种,自己编写和从父类继承。能够从父类继承下来构造函数的条件是:

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

重写

重写实例属性

实例属性的重写一方面可以重写Getter和Setter访问器,另一方面可以重写属性观察者。(在子类继承父类的时候,可以通过Getter和Setter访问器重写父类的存储属性和计算属性)。

  • 重写Getter和Setter访问器
class Rectangle {
    var width: Double
    var height: Double
    
    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }
}

class SubRectange: Rectangle {
    var name: String
    init(name: String) {
        self.name = name
        super.init(width: 1.0, height: 2.0)
    }
    
    override var width: Double {
        get {
           return super.width
        }
        set {
            super.width = newValue
        }
    }
}

从上述代码可见,子类并不存储属性,数据存储在父类的存储属性中

  • 重写属性观察者
class Rectangle {
    var width: Double
    var height: Double
    
    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }
}

class SubRectange: Rectangle {
    var name: String
    init(name: String) {
        self.name = name
        super.init(width: 1.0, height: 2.0)
    }
    
    override var width: Double {
        willSet {
            print(newValue)
        }
        didSet {
            print(oldValue)
        }
    }
}

注意:一个属性重写了观察者之后,就不能同时对Getter和Setter访问器进行重写。另外,常量属性和只读计算属性也都不能重写属性观察者。

重写静态属性

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

import Foundation

class Rectangle {
    class var staticProp: Double {
        return 100.00
    }
}

class SubRectange: Rectangle {
    override class var staticProp: Double {
        return 10.0
    }
}
重写实例方法与重写静态方法

重写实例方法与重写实例属性类似,需要在前面加上override关键字。
重写静态方法与重写静态属性类似。使用class和static关键字修饰,但是使用哪一个要看子类中是否需要重写该属性。class修饰的属性可以被重写,static修饰的属性不可以被重写。
代码示例:

class Rectangle {
    
    func firstAction() {
        print("被重写的实例方法")
    }
    class func secondAction() {
        print("被重写的静态方法")
    }
}

class SubRectange: Rectangle {
    override func firstAction() {
        print("子类重写父类的实例方法")
    }
 
    override class func secondAction() {
        print("子类重写父类的静态方法")
    }
}

重写下标

重写下标是重写下标的Setter和Getter访问器

class Rectangle {
    let rows: Int
    let columns: Int
    var grid: [Int]
    
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array.init(repeating: 0, count: rows * columns)
    }
    
    subscript(row: Int, col: Int) -> Int {
        get {
            return grid[row * columns + col]
        }
        set {
            grid[row * columns + col] = newValue
        }
    }
}

class SubRectange: Rectangle {
    override subscript(row: Int, col: Int) -> Int {
        get {
            return grid[row * columns + col]
        }
        set {
            grid[row * columns + col] = newValue
        }
    }
}

使用final关键字

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


屏幕快照 2017-12-27 上午7.57.49.png

类型检查和类型转换

//说明:person是父类,Worker和Student继承自person
let p1: Person = Student(name:"tom",  age:13, school: "清华")
let p2: Person = Worker(name:"tom",  age:12, school: "北大")
let p3: Person = Person(name:"tom",  age:11)
let p4: Student = Student(name:"tom",  age:13, school: "清华")
let p5: Worker = Worker(name:"tom",  age:12, school: "北大")
  1. 使用is进行类型检查
    is操作符可以判断一个实例是否是某个类的类型,如果实例是目标类型,结果返回true,否则返回false。
//例如:
if p1 is Worker {
    print("p1 是 Worker 类型")
}
  1. 使用as、as!和as?进行类型转换
    并不是所有的类型都能进行类型转换,类型转换一定发生在继承的前提下。
    类型转换有两个方向
    1. 向下转型:将父类型转换为子类型,这种转换成为向下转型
    2. 向上转型:将子类型转换为父类型,这种转换成为向上转型

注意:通常情况下类型转换都是向下转型,向上转型很少。

  • as操作符
    as操作符仅仅用于向上转型,所以使用情况较少。例如:
//将Student类型的p4向上转换为Person类型的P41.向上转换通常可以省略as Person
let p41: Person = p4 as Person
  • as! 操作符
    向下类型转换。使用as!操作符在类型转换过程中对可选值进行拆包,转换结果为非可选类型。主要有如下两种情况:将非可选类型转换为非可选类型和将可选类型转换为非可选类型。
//1、将非可选类型转换为非可选类型
let p11 = p1 as! Student
//p2本质上是Worker类型,在转换过程中,如果不能转换为目标类型,将会报错
let p12 = p2 as! Student

//2、将可选类型转换为非可选类型
let p6: Person? = Student(name:"tom",  age:13, school: "清华")
let p66 = p6 as! Student

注意:
1、使用as!操作符时,在转换过程中,如果不能转换为目标类型,将会出现运行时错误。
Could not cast of type 'Worker' to 'Student'
2、如果对象本身为nil,转换结果要视是否为可选类型而定。例如如果将p6设置为nil,将会出现运行时错误。
fatal error: unexpectedly found nil while unwrapping an optional value

  • as? 操作符
    向下类型转换。使用as? 操作符在类型转换过程中不进行拆包,转换结果为可选类型。主要有如下两种情况:将非可选类型转换为可选类型和将可选类型转换为可选类型。
//1、将非可选类型转换为可选类型
let p11 = p1 as! Student
let p12 = p2 as! Student  //p2为Worker类型,所以结果为nil
//2、将可选类型转换为可选类型
let p6: Person? = Student(name:"tom",  age:13, school: "清华")
let p11 = p6 as! Student

as? 操作符是不确定类型转换是否能成功的情况下使用,如果成功,结果是可选类型。如果我们确定类型转换一定能成功,可以使用as!操作符。在转换同时进行隐形拆包。

  1. 使用AnyObject 和 Any类型
    AnyObject 和 Any表示两种不确定的类型。AnyObject可以表示任何类的类型,Any可以表示任何类型,包括类和其他数据类型,也包括Int等基本数据类型,当然也包括AnyObject

在OC和Swift混编时,OC的id类型和swift的AnyObject类型可以互换,但是两者有本质区别。id类型是泛型,可以代表任何对象指针类型。编译时编译器不检查id类型,是动态的。而swift的AnyObject是一个实实在在表示类的类型,编译时编译器会检查AnyObject类型。

相关文章

  • 一阶段day16-01面向对象

    Python中类支持继承,并且支持多继承 一、继承 1、什么是继承 父类(超类):被继承的类子类:继承父类的类继承...

  • 2018-10-19面向对象和pygame

    类的继承 python中的类支持继承,并且支持多继承 1.什么是继承 父类(超类):被继承的类子类:继承的类继承就...

  • python零基础13:类的继承和定制

    类的定制和继承是什么? 类的继承 类的定制 类的继承要怎么写? 继承基础语法 继承之多层继承 继承的多重继承 类的...

  • Day16总结:面向对象和pygame

    类的继承 python中类支持继承,并且支持多继承 1.什么是继承 父类(超类):被继承的类子类:去继承父类的类继...

  • python 面向对象和pygame

    一、类的继承 python中类支持继承,并且支持多继承 1.什么是继承父类(超类):被继承的类子类:去继承父类的类...

  • 10.19 day16面向对象和pygame

    1.类的继承python中类 支持继承,并且支持多继承()1.什么是继承父类(超类):被继承的类 子类:继承的类,...

  • Day16-面向对象和pygame

    一、类的继承 python中类支持继承,并且支持多继承 1.什么是继承 父类(超类):被继承的类 子类:去继承父类...

  • 2018-10-19继承、重写、内存管理和认识pygame

    一、类的继承 Python中类支持继承,并且支持多继承 1、什么是继承 父类(超类):被继承的类子类:去继承父类的...

  • day16

    类的继承 python中的类支持继承,并且支持多继承() 1.什么是继承 父类(超类):被继承的类子类:去继承的类...

  • day16

    一、类的继承python中类支持继承,并且支持多继承 1.什么是继承父类(超类):被继承的类子类:去继承父类的类继...

网友评论

      本文标题:类继承

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