继承是面向对象编程的关键功能之一。它允许用户从现有类(基类)创建一个新类(派生类)。
派生类继承了基类的所有功能,并且可以拥有自己的其他功能。
在详细介绍Kotlin继承之前,建议您阅读以下两篇文章:
- Kotlin类和对象
- Kotlin主要建设者
为什么要继承?
假设在您的应用程序中需要三个字符 -一个数学老师 ,一个足球运动员和一个商人 。
由于所有字符都是人,因此他们可以走路和说话。但是,他们也有一些特殊技能。数学老师可以教数学 ,足球运动员可以踢足球 ,商人可以经营企业 。
您可以单独创建三个可以走路,说话和执行其特殊技能的课程。
在每个类中,您将为每个字符复制相同的用于走路和说话的代码。
如果要添加新功能-eat,则需要为每个字符实现相同的代码。这很容易导致出错(复制时)和重复代码。
如果我们拥有一个Person
类,使其具有基本的功能(例如说话,走路,吃饭,睡觉),并根据我们的字符为这些功能添加特殊技能,这将容易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中的类是最终的。如果您熟悉Java,那么您将知道最终类不能被子类化。通过在类上使用开放注释,编译器允许您从其派生新类。
示例: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) {
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.
这里,从Person
类派生了两个MathTeacher
和Footballer
类。
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
,并且这两个参数都在基类的主构造函数中初始化。这是另一个例子:
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
) { val f1 = Footballer(29, "Cristiano", "LA Galaxy") } - 如果没有主构造函数,则每个基类都必须初始化基类(使用super关键字),或委派给执行该操作的另一个构造函数。例如,
fun main(args: Array
) { 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) {
val girl = Girl()
girl.displayAge(31)
}
运行该程序时,输出为:
My fake age is 26.
在这里, girl.displayAge(31)
调用派生类Girl
的displayAge()
方法。
您可以通过类似的方式覆盖基类的属性。
在检查以下示例之前,请访问Kotlin的Kotlin吸气器和设置器的工作方式。
// 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) {
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) {
val girl = Girl()
girl.displayAge(31)
}
运行该程序时,输出为:
My age is 31.
My fake age is 26.