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()
    }
}
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work2 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work2 天前
ch21 签名、校验与发版
kotlin
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui