编辑这个页面须要登录或更高权限!
在本文中,您将学习继承。更具体地说,什么是继承以及如何在Kotlin中实现使用 继承(借助示例)。
继承是面向对象编程的关键功能之一。它允许用户从现有类(基类)创建一个新类(派生类)。
派生类继承了基类的所有功能,并且可以拥有自己的其他功能。
在详细介绍Kotlin继承之前,建议您阅读以下两篇文章:
假设在您的应用程序中需要三个角色-一个数学老师(MathTeacher),一个足球运动员(Footballer)和一个商人(Businessman)。
由于所有角色都是人,因此他们可以 走路 和 说话。但是,他们也有一些特殊技能。数学老师可以教数学(teachMath),足球运动员可以踢足球(playFootball),商人可以经营企业(runBusiness)。
您可以单独创建三个可以走路,说话和执行其特殊技能的类。
在每个类中,您将为每个角色复制相同的步行和说话代码。
如果要添加新特性 - eat(吃),则需要为每个角色实现相同的代码。这很容易导致出错(复制时)和重复代码。
如果我们有一个具有基本功能的 Person 类,比如说,走,吃,睡,并根据我们的角色为这些功能添加特殊技能,那就容易多了。这是通过继承完成的。
使用继承,现在您不需要为每个类实现相同的walk()、talk()和eat()代码。 你只需要继承它们就行了。
因此,对于MathTeacher(派生类),您可以继承Person(基类)的所有功能,并添加一个新功能 teachingMath()。 同样,对于Footballer类,您继承了Person类的所有功能,并添加了新功能 playFootball(),依此类推。
这使您的代码更简洁,可理解且可扩展。
重要的是要记住:在处理继承时,每个派生类都应满足其是否为“基”类的条件。 在上面的示例中,MathTeacher是一个 Person(人),Footballer 是一个 Person(人)。 您不能认为“商人(Businessman)就是企业(Business)”。
让我们尝试在代码中实现以上讨论:
open class Person(age: Int) { //吃饭、说话、走路的代码 } class MathTeacher(age: Int): Person(age) { //数学教师的其他特点 } class Footballer(age: Int): Person(age) { //足球运动员的其他特点 } class Businessman(age: Int): Person(age) { // 商人的其他特征 }
这里,Person是基类,而 MathTeacher,Footballer 和 Businessman 类则是从 Person 类派生的。
注意,关键字 open 在基类 Person 之前,这点非常重要。
默认情况下,Kotlin中的类是最终的。 如果您熟悉Java,那么您将知道最终类不能被子类化。 通过在类上使用 注解,编译器允许您从其派生新类。
open class Person(age: Int, name: String) { init { println("我的名字是 $name.") println("我的年龄是 $age") } } class MathTeacher(age: Int, name: String): Person(age, name) { fun teachMaths() { println("我在小学教书。") } } class Footballer(age: Int, name: String): Person(age, name) { fun playFootball() { println("我为洛杉矶银河队效力。") } } fun main(args: Array<String>) { val t1 = MathTeacher(25, "Jack") t1.teachMaths() println() val f1 = Footballer(29, "Christiano") f1.playFootball() }
运行该程序时,输出为:
我的名字是 Jack. 我的年龄是 25 我在小学教书。 我的名字是 Cristiano. 我的年龄是 29 我为洛杉矶银河队效力。
这里,从 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()函数。
创建类的对象 f1 时,该程序的工作原理类似。 它执行基类的init块。 然后,使用语句f1.playFootball()调用 Footballer 类的playFootball()方法。
如果该类具有主要构造函数,则必须使用主要构造函数的参数来初始化基类。在上面的程序中,两个派生类都有两个参数 age 和 name,并且这两个参数都在基类的主构造函数中初始化。
这是另一个实例:
open class Person(age: Int, name: String) { // some code } class Footballer(age: Int, name: String, club: String): Person(age, name) { init { println("年龄为 $age 的足球运动员 $name,为 $club 效力。") } fun playFootball() { println("我正在踢足球。") } } fun main(args: Array<String>) { val f1 = Footballer(29, "Cristiano", "LA Galaxy") }
在此,派生类的主要构造函数具有3个参数,而基类具有2个参数。请注意,基类的两个参数均已初始化。
如果没有主构造函数,则每个基类都必须初始化基类(使用super关键字),或者委托给另一个执行此操作的构造函数。 例如
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) { } }
要了解有关该程序如何工作的更多信息,请访问Kotlin 次构造函数。
如果基类和派生类包含具有相同名称的成员函数(或属性),则可能需要使用 override 关键字覆盖派生类的成员函数,并对基类的成员函数使用 open 关键字。
// 空的主构造函数 open class Person() { open fun displayAge(age: Int) { println("我的年龄是 $age.") } } class Girl: Person() { override fun displayAge(age: Int) { println("我的虚拟年龄是 ${age - 5}.") } } fun main(args: Array<String>) { val girl = Girl() girl.displayAge(31) }
运行该程序时,输出为:
的虚拟年龄是 26.
在此,girl.displayAge(31) 调用派生类 Girl 的 displayAge() 方法。
您可以通过类似的方式覆盖基类的属性。
在学习以下示例之前,可以访问 Kotlin的 getter 和 setter 查看工作方式。
//空的主要构造函数 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("我的虚拟年龄是 ${girl.age}.") }
运行该程序时,输出为:
我的虚拟年龄是 26.
正如您看到的,我们分别在派生类和基类中为 age 属性使用了 override 和 open 关键字。
您可以使用super关键字从派生类中调用基类的函数(和访问属性)。这是如何做:
open class Person() { open fun displayAge(age: Int) { println("我的实际年龄是 $age.") } } class Girl: Person() { override fun displayAge(age: Int) { //调用基类的函数 super.displayAge(age) println("我的虚拟年龄是 ${age - 5}.") } } fun main(args: Array<String>) { val girl = Girl() girl.displayAge(31) }
运行该程序时,输出为:
我的实际年龄是 31. 我的虚拟年龄是 26.