文章目录
I . 属性覆盖基本方式
II . 属性覆盖的四种情况
III . 常量 ( val ) / 变量 ( var ) 属性覆盖
IV . 子类初始化时考虑覆盖属性的使用
I . 属性覆盖基本方式
1 . 属性覆盖 : 属性覆盖与方法覆盖的方式基本相同 ;
① 属性覆盖前提 : 在父类中使用 open 修饰的属性 , 可以在子类中被覆盖 ;
② 属性覆盖方式 : 在子类中使用 override 修饰被覆盖的属性 ;
2 . 属性覆盖示例 :
open class Father { //被覆盖的属性需要使用 open 修饰 open var age : Int = 60 } class Son : Father() { //子类覆盖的属性需要使用 override 修饰 override var age : Int = 28 }
II . 属性覆盖的四种情况
1 . 子类的覆盖属性的要求 : 子类中覆盖的属性有特定的要求 , 不能是延迟加载属性 , 下面列举几种常见的方式 ;
2 . 覆盖属性初始化 : 子类中使用 override 覆盖的属性需要设定一个初始值 ;
open class Father { open var age : Int = 60 } class Son : Father() { override var age : Int = 18 }
3 . 覆盖属性设置 getter / setter 方法 : 子类中使用 override 覆盖的属性设置对应的 getter 和 setter 方法 ;
open class Father { open var age : Int = 60 } class Son : Father() { override var age : Int get() { TODO() } set(value) {} }
4 . 将子类和覆盖属性声明成抽象化的 : 子类可以声明成抽象类 , 其 override 属性也可以声明成抽象属性 ;
open class Father { open var age : Int = 60 } abstract class Son : Father() { abstract override var age : Int }
5 . 覆盖属性声明在子类主构造函数中 : 可以将子类中覆盖的父类属性声明在主构造函数中 , 如下示例 :
open class Father { open var age : Int = 60 } class Son(override var age: Int) : Father() { }
III . 常量 ( val ) / 变量 ( var ) 属性覆盖
1 . 常量覆盖 : 父类中 val 修饰的常量属性可以在子类中使用 val 或 var 覆盖 ;
open class Father { open val name : String = "Tom" open val age : Int = 60 } class Son : Father() { //1 . 父类常量常规情况下被子类重写成常量 override val name : String = "Jerry" //2 . 父类常量可以被子类重写成变量 override var age : Int = 18 }
2 . 变量覆盖 : 父类中的 var 属性可以被子类中的 var 属性覆盖 , 不能被 val 属性覆盖 ;
① 代码示例 ( 正确 ) :
open class Father { open var age : Int = 60 } class Son : Father() { //父类变量 只能 被子类重写成变量 , 不能被重写成常量 override var age : Int = 18 }
② 错误示例 ( 错误 ) :
3 . 覆盖原理 :
① 常量覆盖 : 常量属性只有 get 方法 , 没有 set 方法 ; 子类将常量 override 成变量 , 就是为其多写了一个 set 方法 ;
② 变量覆盖 : 但是子类不能讲一个变量重写成常量 , 父类的方法可以修改添加 , 但是不能删除 ;
IV . 子类初始化时考虑覆盖属性的使用
1 . 子类初始化 : 子类初始化时 , 要先将父类进行初始化 , 然后开始初始化子类 ;
2 . 父类初始化流程 :
① 父类构造函数 : 先调用主构造函数 / 次构造函数 ;
② 父类初始化 : 然后调用父类属性构造器 和 init 初始化代码块 , 这两个模块优先级相同 , 根据其代码顺序从上到下执行 ;
3 . 子类初始化流程 : 执行完父类初始化后 , 开始执行子类初始化 ;
① 子类构造函数 : 执行子类构造函数剩余部分 ( 如果有的话 , 一般是次构造函数 ) ;
② 子类初始化 : 执行子类属性构造器 和 init 初始化代码块 代码 , 这两个模块优先级相同 , 根据其代码顺序从上到下执行 ;
4 . 初始化过程中的覆盖属性 : 这里加入对覆盖属性的考虑 , 父类初始化过程中 , 子类覆盖的属性还没有初始化 , 父类的 open 属性可能在子类初始化过程中被修改 ;
5 . 最佳实践 : 在父类中 , 尽量不在 构造函数 , init 初始化代码块 , 属性初始化 时使用被 open 关键字修饰的可覆盖属性成员 , 因为该值不稳定 , 会增加不确定因素 ;