Flutter 类和对象(二):继承

Flutter 类和对象(一):类

继承

在 Kotlin 中,所有的类都有个共同的超类 Any,即使不显示继承,也会默认继承:

kotlin 复制代码
class Example // Implicitly inherits from Any

Any 有三个方法:equals()、hashCode()和 toString()。因此,所有的Kotlin类都有这三个方法。

Kotlin 类默认是 final 的,所以默认是不可以被继承的。如果想要一个类变的可被继承,就需要使用open 标记:

kotlin 复制代码
open class Base // Class is open for inheritance

在类头使用冒号来显示的指定超类:

kotlin 复制代码
open class Base(p: Int)

class Derived(p: Int) : Base(p)

假如派生类有主构造函数,那么基类必须根据其参数在该主构造函数中进行初始化。

如果派生类没有主构造函数,那么每个次构造函数都必须使用 super 关键字来初始化基类类型,或者必须委托给另一个能完成该初始化操作的构造函数。

在这种情况下,不同的次构造函数可以调用基类的不同构造函数。

kotlin 复制代码
class MyView : View {
    constructor(ctx: Context) : super(ctx)

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

方法重写

Kotlin 要求对可重写成员和重写操作使用显式修饰符:

对于重写的 draw 来说,override 标识符是必须的。如果没有的话,编译器会报错。Shape 的fill方法没有 open 标识符,所以编译器报了错。当 open 修饰符被添加到 final 类(的成员上时,它不会产生任何效果。

被标记为 override 的成员本身是可重写的(open),因此它可以在子类中被再次重写。如果希望禁止再次重写,可使用 final 修饰符:

kotlin 复制代码
open class Rectangle() : Shape() {
    final override fun draw() { /*...*/ }
}

属性重写

属性的重写机制与方法的重写机制相同。在超类中声明的属性若要在派生类中重新声明,必须以 override 开头,且它们必须具有兼容的类型。

每个已声明的属性都可以被带有初始化器的属性或带有 get 方法的属性重写:

kotlin 复制代码
open class Shape {
    open val vertexCount: Int = 0
}

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

开发者可以使用 var 来重写 val,但是反过来不行。这是因为 val 属性本身声明了一个 get 方法,如果使用 var 来重写的的话,相当于子类在父类的基础上扩展了 set 方法。如果父类是 var ,var 是带有get 和 set 方法的,如果子类是 val 的话,相当于只有 get,这就违反了继承的规则。

开发者可以在主构造函数中使用 override 关键字:

kotlin 复制代码
interface Shape {
    val vertexCount: Int
}

class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices

class Polygon : Shape {
    override var vertexCount: Int = 0  // Can be set to any number later
}

子类初始化顺序

构造子类的对象时,基类的初始化会在子类的初始化之前执行。

kotlin 复制代码
open class Base(val name: String) {

    init { println("Initializing a base class") }

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

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

    init { println("Initializing a derived class") }

    override val size: Int =
        (super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
// Argument for the base class: Java
// Initializing a base class
// Initializing size in the base class: 4
// Initializing a derived class
// Initializing size in the derived class: 10

1.首先执行 Derived 的构造中传递给 Base 的表达式

name.replaceFirstChar { it.uppercase() }.also { ... }

此时会打印:Argument for the base class: Java

2.执行基类的初始化 执行基类的 init 块:Initializing a base class

初始化基类的 size 属性:计算 name 长度("Java" 长度为 4),打印:Initializing size in the base class: 4

3.执行子类的初始化 执行派生类的 init 块:Initializing a derived class

初始化派生类的 size 属性:计算 super.size (4) + lastName.length (6) 得到 10,打印:Initializing size in the derived class: 10

调用父类的实现

通过 super 关键字就可以在子类中访问父类的方法和属性:

kotlin 复制代码
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(其中 Outer 需替换为实际的外部类类名):

kotlin 复制代码
class FilledRectangle: Rectangle() {
    override fun draw() {
        val filler = Filler()
        filler.drawAndFill()
    }

    inner class Filler {
        fun fill() { println("Filling") }
        fun drawAndFill() {
            super@FilledRectangle.draw() // Calls Rectangle's implementation of draw()
            fill()
            println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // Uses Rectangle's implementation of borderColor's get()
        }
    }
}

Drawing rectangle  // 来自 Rectangle 的 draw()
Filling            // 来自 Filler 的 fill()
Drawn a filled rectangle with color black  // 使用 Rectangle 的 borderColor

重写规则

在 Kotlin 中,实现继承受以下规则约束

  • 若一个类从其直接超类( immediate superclasses )中继承了同一成员的多个实现,该类必须重写这个成员并提供自己的实现。
  • 若要指明新实现中复用的是哪个超类的继承实现,需使用 super 关键字并结合尖括号中的超类名进行限定,例如 super(其中 Base 需替换为实际的超类类名)。
kotlin 复制代码
open class Rectangle {
    open fun draw() { /* ... */ }
}

interface Polygon {
    fun draw() { /* ... */ } // interface members are 'open' by default
}

class Square() : Rectangle(), Polygon {
    // The compiler requires draw() to be overridden:
    override fun draw() {
        super<Rectangle>.draw() // call to Rectangle.draw()
        super<Polygon>.draw() // call to Polygon.draw()
    }
}
相关推荐
掘金一周19 小时前
2025年还有前端不会Nodejs ?| 掘金一周 9.25
android·前端·后端
2501_9151063219 小时前
iOS 混淆与机器学习模型保护 在移动端保密权重与推理逻辑的实战指南(iOS 混淆、模型加密、ipa 加固)
android·人工智能·机器学习·ios·小程序·uni-app·iphone
低调小一19 小时前
从Android到iOS:启动监控实现的跨平台技术对比
android·ios·cocoa
雨白19 小时前
使用 Jetpack Compose 构建一个整洁架构笔记应用
android·android jetpack·mvvm
2501_9159090619 小时前
iOS 26 耗电检测实战攻略,如何测电量掉速、定位高耗能模块与优化策略(适用于 uni-app 原生 App)
android·ios·小程序·uni-app·cocoa·iphone·webview
2501_9159214319 小时前
iOS 26 性能测试实战,如何评估启动速度、CPUGPU 负载、帧率与系统资源适配(uni-app 与 iOS 原生应用性能方案)
android·ios·小程序·uni-app·cocoa·iphone·webview
wj07184215420 小时前
android 内存优化
android
臣臣臣臣臣什么臣20 小时前
uni-app 多文件上传:直接循环调用 uni.uploadFile 实现(并行 / 串行双模式)
android·前端
Kapaseker21 小时前
每个Kotlin开发者应该掌握的最佳实践,第三趴
android·kotlin
lar_slw21 小时前
flutter json转实体类
android·flutter·json