Kotlin基础语法 九、类的继承

1.超类(Any)

在Kotlin中,所有的类都继承于Any类,如果没有显示声明父类就默认继承自Any。

class Person  // Person默认继承自Any

Any这个类给我们提供了equals()、hashCode()、toString()这三个方法,
如下:


public open class Any {
    
    
    public open operator fun equals(other: Any?): Boolean

    public open fun hashCode(): Int

    public open fun toString(): String
}

2.继承规则

默认情况下,Kotlin 类是final的,表示不能被继承。 要使一个类可继承,请用 open 关键字标记它

final class Person //final是默认的,自动省略,表示该类不能被继承
open class Person // open是需要显示添加的,表示该类可以被继承

Java继承是使用extends关键字,而Kotlin是使用:符号
示例:


open class Base(p: Int)

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

3. 继承类的构造函数

子类中的代码可以使用 super 关键字调用其父类的函数与属性访问器的实现。

Kotlin类,可以有一个主构造函数,或者多个次构造函数。
1.)当子类无主构造函数时,则每个辅助构造函数必须使用super关键字初始化父类的构造函数,或者委托给另一个构造函数。

示例:这里举例在Android中常见的自定义View实现,我们熟知,当我们指定一个组件是,一般实现父类的三个构造函数。

class MyView : View {
    
    

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
            : super(context, attrs, defStyleAttr)

}

2.)如果子类存在主构造函数
当子类存在主构造函数时,主构造函数一般实现父类中参数最多的构造函数,参数少的构造函数则用this关键字引用该构造函数即可

示例如下:

class MyView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int)
    : View(context, attrs, defStyleAttr) {
    
    

    constructor(context: Context?) : this(context,null,0)

    constructor(context: Context?,attrs: AttributeSet?) : this(context,attrs,0)
}

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

官方示例:

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")
    }
}

运行结果是:(also的作用是 对一个对象进行操作并返回该对象)
Argument for Base: Hello
Initializing Base
Initializing size in Base: 5
Initializing Derived
Initializing size in Derived: 10
可以看到 先初始化了父类的初始化代码块和属性,然后再初始化了子类的初始化代码块和属性。

4. 方法的重写和重载

重写(覆盖)父类里的方法,称为重写
父类中的函数没有用open修饰符修饰时,默认是final的,不能被重写
当父类中的函数,没有用open修饰符修饰的时候,子类中出现的函数的函数名不能与父类中没有用open修饰符修饰的函数的函数名相同,不管实现类中的该函数有无override修饰符修饰。

示例:Circle.draw() 函数上必须加上 override 修饰符。如果没写,编译器将会报错。 如果函数没有标注 open 如 Shape.fill(),那么子类中不允许定义相同签名的函数, 不论加不加 override。将 open 修饰符添加到 final 类(即没有 open 的类)的成员上不起作用。

open class Shape {
    
    
    open fun draw() {
    
     /*……*/ }
    fun fill() {
    
     /*……*/ }
}
class Circle() : Shape() {
    
    
    override fun draw() {
    
     /*……*/ }
}

方法重载
方法的重载。即函数名相同,函数的参数不同的情况。和Java是相同的。

5. 属性的重写

属性也默认是final 修饰的,如果想重写属性,也需要用open 修饰
属性覆盖与方法覆盖类似;在父类中声明,然后在子类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。 每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖。

示例:

open class Person {
    
    
    open val userWork = "无职业"
}
class Teacher : Person() {
    
    
    override val userWork : String="教师"
}

你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行。 这是允许的,因为一个 val 属性本质上声明了一个 get 方法, 而将其覆盖为 var 只是在子类中额外声明一个 set 方法。
Getter()函数慎用super关键字,super代表父类
重写属性的时候如果用get() = super.xxx,这样的话,不管你是否重新为该属性赋了新值,还是支持setter(),在使用的时候都调用的是基类中的属性值。
如下:

open class Person {
    
    
    open val userWork = "无职业"
}
class Teacher : Person() {
    
    
    override var userWork : String="教师"
        get() = super.userWork
        set(value) {
    
    
            field = value
        }
}
val teacher = Teacher()
Log.e("职业是",teacher.userWork)

上述代码的运行结果是:
职业是: 无职业

猜你喜欢

转载自blog.csdn.net/weixin_43864176/article/details/123668609
今日推荐