📜  Kotlin继承

📅  最后修改于: 2020-10-05 14:50:39             🧑  作者: Mango

在本文中,您将学习继承。更具体地说,什么是继承以及如何在Kotlin中实现继承(借助示例)。

继承是面向对象编程的关键功能之一。它允许用户从现有类(基类)创建一个新类(派生类)。

派生类继承了基类的所有功能,并且可以拥有自己的其他功能。

在详细介绍Kotlin继承之前,建议您阅读以下两篇文章:

  • Kotlin类和对象
  • Kotlin主要建设者

为什么要继承?

假设在您的应用程序中需要三个字符 -一个数学老师 ,一个足球运动员和一个商人

由于所有字符都是人,因此他们可以走路和说话。但是,他们也有一些特殊技能。数学老师可以教数学 ,足球运动员可以踢足球 ,商人可以经营企业

您可以单独创建三个可以走路,说话和执行其特殊技能的课程。

不使用继承共享相同功能的类的示例。

在每个类中,您将为每个字符复制相同的用于走路和说话的代码。

如果要添加新功能-eat,则需要为每个字符实现相同的代码。这很容易导致出错(复制时)和重复代码。

如果我们拥有一个Person类,使其具有基本的功能(例如说话,走路,吃饭,睡觉),并根据我们的字符为这些功能添加特殊技能,这将容易Person 。这是使用继承完成的。

OOP中的继承示例

使用继承,现在您不必为每个类的walk()talk()eat()实现相同的代码。您只需要继承它们即可。

因此,对于MathTeacher (派生类),您可以继承Person (基类)的所有功能,并添加一个新的功能teachMath() 。同样,对于Footballer类,您继承了Person类的所有功能,并添加了新功能playFootball() ,依此类推。

这使您的代码更简洁,可理解且可扩展。

重要的是要记住:在处理继承时,每个派生类都应满足其是否为“基”类的条件。在上面的示例中, MathTeacher PersonFootballer 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是基类,而MathTeacherFootballerBusinessman是从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类派生了两个MathTeacherFootballer类。

Person类的主要构造函数声明了两个属性: agename ,并且具有一个初始化程序块。基类Person的初始化程序块(和成员函数)可以由派生类( MathTeacherFootballer )的对象访问。

派生类MathTeacherFootballer具有自己的成员函数teachMaths()playFootball() 。这些功能只能从其各自类别的对象中访问。


创建MathTeacher类的对象t1时,

val t1 = MathTeacher(25, "Jack")

参数将传递给主构造函数。在Kotlin中,创建对象时会调用init块。由于MathTeacher是从Person类派生的,因此它将在基类(Person)中查找初始化程序块并执行它。如果MathTeacher具有init块,则编译器还将执行派生类的init块。

接下来,使用t1.teachMaths()语句调用对象t1teachMaths() 函数 。

创建Footballer类的对象f1时,该程序的工作原理类似。它执行基类的init块。然后,使用语句f1.playFootball()调用Footballer类的playFootball()方法。


重要说明:Kotlin继承
  • 如果该类具有主要构造函数,则必须使用主要构造函数的参数来初始化基类。在上面的程序中,两个派生类都有两个参数agename ,并且这两个参数都在基类的主构造函数中初始化。

    这是另一个例子:

    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)调用派生类GirldisplayAge()方法。


您可以通过类似的方式覆盖基类的属性。

在检查以下示例之前,请访问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属性使用了overrideopen关键字。


从派生类调用基类成员

您可以使用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.