- 调用父类构造函数
- 使用主构造函数
- 使用此构造函数重载
- 使用参数默认值调用构造函数
- 重写成员属性和函数
- 重写成员属性
- 重写成员函数
kotlin中类继承只能是单继承,但是却可以有多个接口实现,这一点与 Java 是一致的。kotlin中子类能够继承父类,那么父类需要声明为 open,在 kotlin 中默认类不能被继承,所以必须声明为 open。
一、调用父类构造函数
当子类实例化时,不仅需要初始化子类成员属性,也需要初始化父类成员属性,初始化父类成员属性需要调用父类构造函数。
open class Person(val name: String,
val age: Int,
val birthDate: Date) { // 主构造函数
constructor(name: String, age: Int): this(name, age, Date()) // 次构造函数
override fun toString(): String {
return ("Person [name=$name, age=$age, birthDate=$birthDate]")
}
}
1、使用主构造函数
class Student(name: String,
age: Int,
birthDate: Date,
val school: String): Person(name, age, birthDate) { // 主构造函数
constructor(name: String, age: Int, school: String): this(name, age, Date(), school) //1️⃣
// constructor(name: String, age: Int, school: String): super(name, age, Date()) 2️⃣
constructor(name: String, school: String): this(name, 18, school)
}
子类继承父类时,子类中一旦声明了主构造函数,那么子类的此构造函数就不能直接调用父类构造函数,只能调用自己的主构造函数。例如上述代码第1️⃣行只能调用this(name, age, Date(), school)
而不能调用super(name, age, Date())
,如第2️⃣行代码是编译不通过的。
2、使用此构造函数重载
class Student: Person {
private var school: String? = null
constructor(name: String,
age: Int,
birthDate: Date,
school: String): super(name, age, birthDate) { // 3️⃣
this.school = school
}
constructor(name: String,
school: String): super(name, 18) { // 4️⃣
this.school = school
}
constructor(name: String, age: Int, school: String): this(name, age, Date(), school)
}
子类继承父类时,子类中如果没有声明主构造函数,则子类的次构造函数能直接调用父类构造函数,如代码第3️⃣行调用父类的主构造函数,代码第4️⃣行调用父类的次构造函数。
3、使用参数默认值调用构造函数
一个类有多个构造函数时,多个构造函数之间构成了重载关系,kotlin 从语法角度是支持重载的,但更推荐采用参数默认值的方式。
class Student: Person {
private var school: String? = null
constructor(name: String,
age: Int = 18,
birthDate: Date = Date(),
school: String): super(name, age, birthDate) {
this.school = school
}
}
这段代码只有一个次构造函数,和4个参数,其中 age 和 birthDate 提供了默认值。这样声明相当于提供了4个构造函数。调用方式如下:
fun main(args: Array<String>) {
val student1 = Student("小三", 20, Date(), "清华大学")
val student2 = Student("小三", birthDate = Date(), school = "清华大学")
val student3 = Student("小三", school = "清华大学")
val student4 = Student("小三", age = 20, school = "清华大学")
}
上面代码只有一个次构造函数,起始也可以有一个主构造函数。
class Student(name: String,
age: Int = 18,
birthDate: Date = Date(),
school: String): Person(name, age, birthDate) {
private var school: String? = null
init {
this.school = school
}
}
二、重写成员属性和函数
子类继承父类后,子类中可能声明了与父类一样的成员属性和函数。
1、重写成语属性
子类成员属性与父类一样,就会重写 (override) 父类中的成员属性,也就是屏蔽了父类成员属性。
open class ParentClass {
open var x: Int = 10
}
class SubClass: ParentClass() {
// 屏蔽父类x成员属性
override var x: Int = 20
fun printX() {
println("x = $x") // 访问的是SubClass的成员属性x
println("super.x = ${super.x}") // 访问ParentClass的成员属性x
}
}
fun main(args: Array<String>) {
val subClass = SubClass()
subClass.printX()
}
// x = 20
// super.x = 10
子类继承父类时,子类可以重写父类总的成员属性,默认情况下属性是不能被重写的,它们需要声明为 open。另外,在子类中要重写属性需要有 override 关键字声明。
注意:由于子类重写了父类的成员属性,所以会造成父类的被重写的成员属性可见性被屏蔽,所以要想在子类中让然使用父类被重写的属性,需要通过关键字 super. 调用。
2、重写成员函数
如果子类函数完全与父类函数相同,即:相同的函数名、相同的参数列表和相同的返回类型,只是函数体不同,这称为子类重写父类函数。
open class ParentClass {
open var x: Int = 0
open fun setValue() {
x = 10 // 6️⃣
}
}
class SubClass: ParentClass() {
// 屏蔽父类x成员属性
override var x: Int = 0
public override fun setValue() {
x = 20
super.setValue()
}
fun display() {
println("x = $x")
println("super.x = ${super.x}")
}
}
fun main(args: Array<String>) {
val subClass = SubClass()
subClass.setValue()
subClass.display()
}
2019-05-31 11:19:32.687 5085-5085/cn.ak.kot I/System.out: x = 10
2019-05-31 11:19:32.687 5085-5085/cn.ak.kot I/System.out: super.x = 0
重写成员函数 与 重写成员属性 是一样,同样会屏蔽父类中被重写函数的可见性;父类中函数同样要声明为 open 的,因为默认是 final 的;子类重写函数也一样要使用关键字 override 修饰;在子类调用父类被重写的函数一样需要通过 super. 函数名。
分析: main 函数中实例化的是 SubClass
类,所以 subClass.setValue()
调用的自然是 SubClass
的 setValue()
函数,而其中又通过 super.setValue()
调用了父类的 setValue()
,但注意代码第6️⃣行中的 x 是对应 SubClass 的 x 属性,因为 ParentClass
的 x 属性被重载导致可见性被屏蔽。所以最终结果是super.x = 0
。
注意:重写后的函数不能比原函数有更严格的可见性(可以相同)。

网友评论