继承是面向对象编程的关键特性之一。它允许用户从现有类(基类)创建一个新类(派生类)。
派生类继承了基类的所有特性,并可以拥有自己的附加特性。
在详细介绍Kotlin继承之前,我们建议您阅读以下两篇文章:
为什么需要继承?
假设在您的应用程序中,您想要三个角色——一位数学老师、一位足球运动员和一位商人。
由于所有角色都是人,他们都可以走路和说话。但是,他们也有一些特殊的技能。数学老师可以教数学,足球运动员可以踢足球,商人可以经营生意。
您可以单独创建三个类,它们都可以走路、说话并执行其特殊技能。

在每个类中,您都需要为每个角色复制相同的代码来走路和说话。
如果您想添加一个新功能——吃饭,您需要为每个角色实现相同的代码。这很容易出错(复制时)并导致代码重复。
如果有一个包含走路、说话、吃饭、睡觉等基本功能的Person
类,然后根据我们的角色为这些功能添加特殊技能,那会容易得多。这就是通过继承来实现的。

通过继承,您现在不必为每个类实现相同的walk()
、talk()
和eat()
代码。您只需要继承它们。
因此,对于MathTeacher
(派生类),您继承了Person
(基类)的所有功能,并添加了一个新功能teachMath()
。同样,对于Footballer
类,您继承了Person
类的所有功能,并添加了一个新功能playFootball()
,依此类推。
这使您的代码更简洁、更易于理解和扩展。
请务必记住: 在使用继承时,每个派生类都应满足其是否“是一个”基类。在上面的例子中,MathTeacher
是Person
,Footballer
是Person
。您不能有这样的说法:Businessman
是Business
。
Kotlin 继承
让我们尝试在代码中实现上述讨论。
open class Person(age: Int) { // code for eating, talking, walking } class MathTeacher(age: Int): Person(age) { // other features of math teacher } class Footballer(age: Int): Person(age) { // other features of footballer } class Businessman(age: Int): Person(age) { // other features of businessman }
这里,Person
是一个基类,而MathTeacher
、Footballer
和Businessman
类是从Person类派生的。
请注意,在基类Person
前面有open
关键字。这很重要。
默认情况下,Kotlin 中的类是 final 的。如果您熟悉 Java,您会知道 final 类不能被继承。通过在类上使用 open 注解,编译器允许您从此类派生新类。
示例:Kotlin 继承
open class Person(age: Int, name: String) {
init {
println("My name is $name.")
println("My age is $age")
}
}
class MathTeacher(age: Int, name: String): Person(age, name) {
fun teachMaths() {
println("I teach in primary school.")
}
}
class Footballer(age: Int, name: String): Person(age, name) {
fun playFootball() {
println("I play for LA Galaxy.")
}
}
fun main(args: Array<String>) {
val t1 = MathTeacher(25, "Jack")
t1.teachMaths()
println()
val f1 = Footballer(29, "Christiano")
f1.playFootball()
}
运行程序后,输出将是
My name is Jack. My age is 25 I teach in primary school. My name is Cristiano. My age is 29 I play for LA Galaxy.
这里,MathTeacher
和Footballer
两个类是从Person
类派生的。
Person
类的主构造函数声明了两个属性:age和name,并且它有一个初始化块。基类Person
的初始化块(以及成员函数)可以被派生类(MathTeacher
和Footballer
)的对象访问。
派生类MathTeacher
和Footballer
分别拥有自己的成员函数teachMaths()
和playFootball()
。这些函数只能从其各自类的对象访问。
当创建MathTeacher
类的对象t1时,
val t1 = MathTeacher(25, "Jack")
参数被传递给主构造函数。在Kotlin中,对象创建时会调用init
块。由于MathTeacher
是从Person
类派生的,它会查找基类(Person)中的初始化块并执行它。如果MathTeacher
有init块,编译器也会执行派生类的init块。
接下来,使用t1.teachMaths()
语句调用对象t1
的teachMaths()
函数。
当创建Footballer
类的对象f1
时,程序的工作方式类似。它执行基类的init块。然后,使用f1.playFootball()
语句调用Footballer
类的playFootball()
方法。
重要提示:Kotlin 继承
- 如果类有主构造函数,则基类必须使用主构造函数的参数进行初始化。在上面的程序中,两个派生类都有两个参数
age
和name
,并且这两个参数都在基类的主构造函数中进行了初始化。
这是另一个示例
这里派生类的主构造函数有3个参数,基类有2个参数。请注意,基类的两个参数都已初始化。open class Person(age: Int, name: String) { // some code } class Footballer(age: Int, name: String, club: String): Person(age, name) { init { println("Football player $name of age $age and plays for $club.") } fun playFootball() { println("I am playing football.") } } fun main(args: Array<String>) { val f1 = Footballer(29, "Cristiano", "LA Galaxy") }
- 如果没有主构造函数,每个派生类都必须初始化基类(使用super关键字),或委托给另一个执行此操作的构造函数。例如,
要了解此程序如何工作,请访问Kotlin 次构造函数。fun main(args: Array<String>) { val p1 = AuthLog("Bad Password") } open class Log { var data: String = "" var numberOfData = 0 constructor(_data: String) { } constructor(_data: String, _numberOfData: Int) { data = _data numberOfData = _numberOfData println("$data: $numberOfData times") } } class AuthLog: Log { constructor(_data: String): this("From AuthLog -> + $_data", 10) { } constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) { } }
重写成员函数和属性
如果基类和派生类包含同名的成员函数(或属性),您可能需要使用override
关键字重写派生类的成员函数,并为基类的成员函数使用open
关键字。
示例:重写成员函数
// Empty primary constructor
open class Person() {
open fun displayAge(age: Int) {
println("My age is $age.")
}
}
class Girl: Person() {
override fun displayAge(age: Int) {
println("My fake age is ${age - 5}.")
}
}
fun main(args: Array<String>) {
val girl = Girl()
girl.displayAge(31)
}
运行程序后,输出将是
My fake age is 26.
这里,girl.displayAge(31)
调用派生类Girl
的displayAge()
方法。
您可以通过类似的方式重写基类的属性。
在查看下面的示例之前,请了解Kotlin 的 getter 和 setter如何工作。
// Empty primary constructor
open class Person() {
open var age: Int = 0
get() = field
set(value) {
field = value
}
}
class Girl: Person() {
override var age: Int = 0
get() = field
set(value) {
field = value - 5
}
}
fun main(args: Array<String>) {
val girl = Girl()
girl.age = 31
println("My fake age is ${girl.age}.")
}
运行程序后,输出将是
My fake age is 26.
正如您所见,我们在派生类和基类中分别为age属性使用了override
和open
关键字。
从派生类调用基类成员
您可以使用super
关键字从派生类调用基类的函数(和访问属性)。方法如下:
open class Person() {
open fun displayAge(age: Int) {
println("My actual age is $age.")
}
}
class Girl: Person() {
override fun displayAge(age: Int) {
// calling function of base class
super.displayAge(age)
println("My fake age is ${age - 5}.")
}
}
fun main(args: Array<String>) {
val girl = Girl()
girl.displayAge(31)
}
运行程序后,输出将是
My age is 31. My fake age is 26.