Kotlin学习笔记10——继承

前言

上一篇,我们学习了Kotlin中的类和对象,今天继续来学习Kotlin中的继承。

Kotlin 继承

Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类:

class Example // 从 Any 隐式继承

Any 默认提供了三个函数:

equals()
hashCode()
toString()

注意:Any 不是 java.lang.Object。
如果一个类要被继承,可以使用 open 关键字进行修饰。

open class Base(p: Int)           // 定义基类
class Derived(p: Int) : Base(p)

构造函数

子类有主构造函数

如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。

open class Person(var name : String, var age : Int){
    
    // 基类
	//...
}
//子类主构造函数中必须初始化基类主构造函数
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
    
    
	//...
}

// 测试
fun main(args: Array<String>) {
    
    
    val s =  Student("Runoob", 18, "S12346", 89)
    println("学生名: ${
      
      s.name}")
    println("年龄: ${
      
      s.age}")
    println("学生号: ${
      
      s.no}")
    println("成绩: ${
      
      s.score}")
}

子类没有主构造函数

如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。

class Student : Person {
    
    

    constructor(ctx: Context) : super(ctx) {
    
    
    } 

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
    
    
    }
}

示例:

/**用户基类**/
open class Person(name:String){
    
    
    /**次级构造函数**/
    constructor(name:String,age:Int):this(name){
    
    
        //初始化
        println("-------基类次级构造函数---------")
    }
}

/**子类继承 Person 类**/
class Student:Person{
    
    

    /**次级构造函数**/
    constructor(name:String,age:Int,no:String,score:Int):super(name,age){
    
    
        println("-------继承类次级构造函数---------")
        println("学生名: ${
      
      name}")
        println("年龄: ${
      
      age}")
        println("学生号: ${
      
      no}")
        println("成绩: ${
      
      score}")
    }
}

fun main(args: Array<String>) {
    
    
    var s =  Student("Runoob", 18, "S12345", 89)
}

输出结果:

-------基类次级构造函数---------
-------继承类次级构造函数---------
学生名: Runoob
年龄: 18
学生号: S12345
成绩: 89

重写

函数重写

在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:

扫描二维码关注公众号,回复: 11947080 查看本文章
/**用户基类**/
open class Person{
    
    
    open fun study(){
    
           // 允许子类重写
        println("我毕业了")
    }
}

/**子类继承 Person 类**/
class Student : Person() {
    
    

    override fun study(){
    
        // 重写方法
        println("我在读大学")
    }
}

fun main(args: Array<String>) {
    
    
    val s =  Student()
    s.study();

}

标记为 override 的成员本身是开放的,也就是说,它可以在子类中覆盖。如果你想禁止再次覆盖,使用 final 关键字。

属性重写

属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。 每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖。

open class Shape {
    
    
    open val vertexCount: Int = 0
}

class Rectangle : Shape() {
    
    
    override val vertexCount = 4
}

你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。 这是允许的,因为一个 val 属性本质上声明了一个 get 方法, 而将其覆盖为 var 只是在子类中额外声明一个 set 方法。

请注意,你可以在主构造函数中使用 override 关键字作为属性声明的一部分。

interface Shape {
    
    
    val vertexCount: Int
}

class Rectangle(override val vertexCount: Int = 4) : Shape // 总是有 4 个顶点

class Polygon : Shape {
    
    
    override var vertexCount: Int = 0  // 以后可以设置为任何数
}

子类继承父类时,不能有跟父类同名的变量,除非父类中该变量为 private,或者父类中该变量为 open 并且子类用 override 关键字重写

派生类初始化顺序

在构造派生类的新实例的过程中,第一步完成其基类的初始化(在之前只有对基类构造函数参数的求值),因此发生在派生类的初始化逻辑运行之前。

open class Base(val name: String) {
    
    

    init {
    
     println("Initializing Base") }

    open val size: Int = 
        name.length.also {
    
     println("Initializing size in Base: $it") }
}

class Derived(
    name: String,
    val lastName: String
) : Base(name.capitalize().also {
    
     println("Argument for Base: $it") }) {
    
    

    init {
    
     println("Initializing Derived") }

    override val size: Int =
        (super.size + lastName.length).also {
    
     println("Initializing size in Derived: $it") }
}

这意味着,基类构造函数执行时,派生类中声明或覆盖的属性都还没有初始化。如果在基类初始化逻辑中(直接或通过另一个覆盖的 open 成员的实现间接)使用了任何一个这种属性,那么都可能导致不正确的行为或运行时故障。设计一个基类时,应该避免在构造函数、属性初始化器以及 init 块中使用 open 成员。

调用超类实现

派生类中的代码可以使用 super 关键字调用其超类的函数与属性访问器的实现:

open class Rectangle {
    
    
    open fun draw() {
    
     println("Drawing a rectangle") }
    val borderColor: String get() = "black"
}

class FilledRectangle : Rectangle() {
    
    
    override fun draw() {
    
    
        super.draw()
        println("Filling the rectangle")
    }

    val fillColor: String get() = super.borderColor
}

在一个内部类中访问外部类的超类,可以通过由外部类名限定的 super 关键字来实现:super@Outer:

class FilledRectangle: Rectangle() {
    
    
    fun draw() {
    
     /* …… */ }
    val borderColor: String get() = "black"
    
    inner class Filler {
    
    
        fun fill() {
    
     /* …… */ }
        fun drawAndFill() {
    
    
            super@FilledRectangle.draw() // 调用 Rectangle 的 draw() 实现
            fill()
            println("Drawn a filled rectangle with color ${
      
      super@FilledRectangle.borderColor}") // 使用 Rectangle 所实现的 borderColor 的 get()
        }
    }
}

尾巴

今天的学习笔记就先到这里了,下一篇我们将学习Kotlin中的拓展
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!

猜你喜欢

转载自blog.csdn.net/abs625/article/details/106904533