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()
    }
}
相关推荐
针叶6 小时前
解决Android Studio查找aar源码的错误
android·gradle·android studio
2501_916008897 小时前
uni-app iOS 应用版本迭代与上架实践 持续更新的高效流程
android·ios·小程序·https·uni-app·iphone·webview
人生游戏牛马NPC1号8 小时前
学习 Android (十八) 学习 OpenCV (三)
android·opencv·学习
zhangphil9 小时前
Android把源Bitmap中心缩放到固定宽高的尺寸,Kotlin
android·kotlin
2501_915909069 小时前
uni-app iOS 性能监控与调试全流程:多工具协作的实战案例
android·ios·小程序·https·uni-app·iphone·webview
魔鬼辣不够辣9 小时前
ADB图片上传轮播
android·adb·kotlin
踏雪羽翼9 小时前
Android 16k页面大小适配
android
用户0910 小时前
Kotlin后端开发指南
android·后端
狂浪天涯10 小时前
Android Security | User-Group-Others 权限模型
android