第12章 继承与多态
类的继承性是面向对象语言的基本特性,多态性的前提是继承性。Kotlin支持继承性和多态性。本章讨论Kotlin继承性和多态性。
12.1 Kotlin中的继承
为了了解继承性,先看这样一个场景:一位面向对象的程序员小赵,在编程过程中需要描述和处理个人信息,于是定义了类Person,如下所示:
//代码文件:chapter12/src/com/a51work6/section1/Person.kt
package com.a51work6.section1
import java.util.*
class Person {
// 名字
val name: String? = null
// 年龄
val age: Int = 0
// 出生日期
val birthDate: Date? = null
val info: String
get() = ("Person [name=$name, age=$age, birthDate=$birthDate]")
}
一周以后,小赵又遇到了新的需求,需要描述和处理学生信息,于是他又定义了一个新的类Student,如下所示:
//代码文件:chapter12/src/com/a51work6/section1/Student.kt
package com.a51work6.section1
import java.util.*
class Student {
// 所在学校
val school: String? = null
// 名字
val name: String? = null
// 年龄
val age: Int = 0
// 出生日期
val birthDate: Date? = null
val info: String
get() = ("Person [name=$name, age=$age, birthDate=$birthDate]")
}
很多人会认为小赵的做法能够理解并相信这是可行的,但问题在于Student和Person两个类的结构太接近了,后者只比前者多了一个属性school,却要重复定义其他所有的内容,实在让人“不甘心”。Kotlin提供了解决类似问题的机制,那就是类的继承,代码如下所示:
//代码文件:chapter12/src/com/a51work6/section1/Student.kt
package com.a51work6.section1
import java.util.*
class Student : Person() { ①
// 所在学校
val school:String? = null
override val info: String
get() =("Person [name=$name,age=$age,birthDate=$birthDate]")
}
//代码文件:chapter12/src/com/a51work6/section1/Person.kt
package com.a51work6.section1
import java.util.*
open class Person {//: Any() { ②
// 名字
val name:String? = null
// 年龄
val age: Int =0
// 出生日期
val birthDate:Date? = null
open val info:String
get() =("Person [name=$name,age=$age,birthDate=$birthDate]")
}
上述代码可见Student类继承了Person类中的成员属性和函数,代码第①行声明Student继承Person,继承使用的冒号(:),冒号前是子类,冒号后是父类。
如果在类的声明中没有使用指明其父类,则默认父类为Any类,kotlin.Any类是Kotlin的根类,所有Kotlin类包括数组都直接或间接继承了Any类,在Any类中定义了一些有关面向对象机制的基本函数,如equals、toString和hashCode等函数。
子类能够继承父类,那么父类需要声明为open,见代码第②行,在Kotlin中默认类不能被继承必须声明为open的。
12.2 调用父类构造函数
当子类实例化时,不仅需要初始化子类成员属性,也需要初始化父类成员属性,初始化父类成员属性需要调用父类构造函数。
修改12.1节示例,父类Person代码如下:
//代码文件:chapter12/src/com/a51work6/section2/Person.kt
package com.a51work6.section2
import java.util.*
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]")
}
}
Person类中有两个构造函数,分别是一个主构造函数和一个次构造函数。子类Student继承Person类有多重实现方式,下面分别介绍一下。
12.2.1 使用主构造函数
在子类Student中可以声明主构造函数和次构造函数。示例代码如下:
//代码文件:chapter12/src/com/a51work6/section2/s1/Student.kt
package com.a51work6.section2.s1
import com.a51work6.section2.Person
import java.util.*
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) // super(name, age, Date()) ②
constructor(name: String, //次构造函数
school: String) :this(name, 18, school) //super(name, 18, Date()) ③
}
上述代码第①行是声明Student的主构造函数,主构造函数中val school: String参数,说明会生成属性school,Person(name,
age, birthDate)表达式是调用父类构造函数。代码第②行是声明Student的次构造函数,this(name, age, Date(), school)是调用自己的主构造函数帮助完成初始化,如果将this(name, age, Date(), school)表达式换成super(name, age, Date())则会发生编译错误,super(name, age, Date())是次构造函数中调用父构造函数。代码第③行也是声明Student的次构造函数,this(name, 18, school)是调用自己的代码第②行的次构造函数帮助完成初始化,如果将this(name, 18, school)表达式换成super(name, 18, Date())则会发生编译错误,super(name, 18, Date())是次构造函数中调用父构造函数。
12.2.2 使用次构造函数重载
在子类Student中可以不声明主构造函数,可以声明多个次构造函数。示例代码如下:
//代码文件:chapter12/src/com/a51work6/section2/s2/Student.kt
package com.a51work6.section2.s2
import com.a51work6.section2.Person
import java.util.*
class Student : Person {
// 所在学校
private var school: String? = null
constructor(name: String,
age: Int,
birthDate: Date,
school: String) :super(name, age, birthDate) { ①
this.school = school
}
constructor(name: String,
age: Int,
school: String) : this(name,age, Date(), school) { ②
this.school = school
}
}
上述代码第①行和第②行都是声明次构造函数,其中代码第①行的次构造函数中super(name, age, birthDate)表达式是调用父构造函数。代码第②行的次构造函数中this(name, age, Date(), school)表达式是调用代码第①行自己的次构造函数。
12.2.3 使用参数默认值调用构造函数
一个类有多个多个造函数时,多个构造函数之间构成了重载关系,Kotlin从语法角度是支持重载的,但更推荐采用参数默认值方式。
示例代码如下:
//代码文件:chapter12/src/com/a51work6/section2/s3/Student.kt
package com.a51work6.section2.s3
import com.a51work6.section2.Person
import java.util.*