Kotlin 学习——类和继承

    在 Kotlin 中,类使用 class 关键字来声明,类声明一般由类名、类头(指定了类型的参数、主构造方法)以及使用 {} 包起来的类体,类体可为空:

// 无构造方法的类
class Demo{
    
}

     当只有一个构造方法时,一般写在类名后面,多个构造方法时,在类名后面的构造方法为主构造方法,如果主构造方法没有任何注解或者可见性修饰符,可以省略 constructor 关键字:

class Demo constructor(name:String) {

}
// 或者
class Demo (name:String) {

}

    主构造方法不能包含任何的代码,初始化的代码要放到 init{} 代码块中,如果有多个 init{} 代码块,按顺序执行:

class Demo (name:String) {

    init {
        println("$name A")
    }
    init {
        println("$name B")
    }
}
// Kotlin 中 main() 默认是新建立一个文件专门跑 main() 方法
fun main(args: Array) {
    Demo("name")
}

    结果:

   Kotlin 学习——类和继承_第1张图片

     如果有主构造方法在类名后面,新的构造方法需要委托给主构造方法,可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:

class Demo (name:String) {
    constructor(name: String,age:Int):this(name){
        println("name:$name,age:$age")
    }
}
// Kotlin 中 main() 默认是新建立一个文件专门跑 main() 方法
fun main(args: Array) {
    Demo("李四",20)
}

    结果:

Kotlin 学习——类和继承_第2张图片

    主构造方法是默认 public 的,如果你想你的构造方法私有化,只需要在 constructor 前面设置 private:

  • 继承:在 Kotlin 中也支持继承,和 Java 中每一个类都继承了 Object 类一样,Kotlin 中每一个类都有一个共同的超类:Any(Any 并不是 java.lang.Object。尤其是,它除了 equals()hashCode()和 toString()外没有任何成员

    在 Kotlin 中,要想类可以被继承,需要使用 open 关键字,该关键字与 Java 中的 final 相反,它允许该类可以被继承,可以说,Kotlin 中的类默认是 final 的:

open class Father(lastName:String)

class Son(lastName: String):Father(lastName)

    当父类有主构造参数时,子类必须要有主构造参数:

open class Father(lastName:String){
    init {
        println("lastName of Father:$lastName")
    }
    
    fun show(){
        println("Hello")
    }
}

class Son(lastName: String):Father(lastName){

    init {
        println("lastName of Son:$lastName")
    }
}

fun main(args: Array) {
    Son("HHHH").show()
}

    结果:

Kotlin 学习——类和继承_第3张图片

    当父类没有主构造方法时,有次构造方法,子类需要使用 super 关键字:

open class Father{
    constructor(lastName:String){
        println("lastName of Father:$lastName")
    }

    fun show(){
        println("Hello")
    }
}

class Son:Father{
    constructor(lastName: String):super(lastName){
        println("lastName of Son:$lastName")
    }
}

fun main(args: Array) {
    Son("HHHH").show()
}

    结果:

Kotlin 学习——类和继承_第4张图片

  • 方法覆盖:而 Kotlin 中方法覆盖时也是一样,需要对父类方法使用 open 方法允许覆盖,然后在子类中对要覆盖的方式使用 override 关键字。
open class Father{
    open fun show(){
        println("I'm Father !")
    }
}

class Son: Father() {
    override fun show() {
        println("I'm Son !")
    }
}
fun main(args: Array) {
    Father().show()
    Son().show()
}

    结果:

Kotlin 学习——类和继承_第5张图片

    因为 override 关键字就表示该方法是可以覆盖的,要想它不能被覆盖,就需要添加一个 final 关键字:

Kotlin 学习——类和继承_第6张图片

    这样的话 show() 方法就不能被覆盖了。

  • 属性覆盖:和方法覆盖类似,父类属性使用 open 关键字,子类属性使用 override 关键字,并且它们必须具备有兼容的类型:
open class Father {
    open val name = "Father"
}
class Son:Father(){
    override val name = "Son"
}
fun main(args: Array) {
    println(Father().name)
    println(Son().name)
}

    结果:

Kotlin 学习——类和继承_第7张图片

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

  • 子类初始化顺序:
open class Father(lastName:String) {
    val a = lastName.length

    open val name = "Father"
    init {
        println("$name:$lastName + $a")
    }
}
class Son(lastName: String):Father(lastName){
    override val name = "Son"
    init {
        println("${super.name}:$lastName")
    }
    init {
        println("$name:$lastName")
    }
}
fun main(args: Array) {
    Son("张")
}

    结果:

Kotlin 学习——类和继承_第8张图片

    由结构可以看到:会先初始化父类,在父类的构造函数执行时,其非 open 属性也被初始化,而 open 属性未被初始化,所有会出现 null:张 + 1,接着才会初始化父类的 open 属性,所有子类就可以使用了,接着会初始化子类的构造方法。所以当我们设计一个父类时,应该避免在构造函数、属性初始化器以及 init 块中使用 open 成员。

  • 调用父类方法:大家都知道 Java 中的 super 关键字,可以使用 super 关键字调用父类的属性和方法,在 Kotlin 中也是一样的:
open class Father{
    open fun show(){
        println("Father")
    }
}

open class Son:Father(){
    override fun show() {
        super.show()
        println("Son")
    }
}
fun main(args: Array) {
    Son().show()
}

    结果:

Kotlin 学习——类和继承_第9张图片

    而在 Kotlin 的内部类中,可以使用 @ClassA.x() 来调用 ClassA 的父类的 x() 方法:

open class Father{
    open fun show(){
        println("Father")
    }
}

open class Son:Father(){
    override fun show() {
        super.show()
        println("Son")
    }
    inner class GrandSon{
        fun g(){
            [email protected]()
        }
    }
}
class GrandSon:Son(){
    override fun show() {
        super.show()
        println("Son")
    }
    inner class GrandSon1{
        fun g(){
            [email protected]()
        }
    }
}

fun main(args: Array) {
    Son().show()
    println("Son 的内部类:")
    Son().GrandSon().g()
    println("GrandSon 的内部类:")
    GrandSon().GrandSon1().g()
}

    结果:

Kotlin 学习——类和继承_第10张图片

  • 覆盖原则:当一个类继承多个父类(只能多继承接口),如果父类中有一样的属性或者方法时,使用 super 要怎么判断调用哪一个父类的属性和方法呢?这个时候就需要使用 super 来判断使用哪一个父类的属性和方法:
open class Father{
    open fun show(){
        println("I'm Father")
    }
}
// 接口是默认 open 的
interface Mother{
    // 接口的方法也是默认 open 的
    fun show(){
        println("I'm Mother")
    }
}
// 和 Java 一样,Kotlin 的类只能继承一个,接口可以继承多个
class Son:Father(),Mother{
    override fun show() {
        super.show()
        super.show()
    }
}

fun main(args: Array) {
    Son().show()
}

    结果:

Kotlin 学习——类和继承_第11张图片

  • 抽象类:在 Kotlin 中的类和某些成员可以声明为 abstract,抽象成员在本类中可以不用实现。 需要注意的是,我们并不需要用 open 关键字来标注一个抽象类或者函数。
abstract class Father{
    abstract fun show()
}

 class Son:Father(){
    override fun show() {
        println("hhhh")
    }
}

fun main(args: Array) {
    Son().show()
}

    结果:


  • 伴生对象:伴生对象有点类似于 Java 中被 static 标注的属性和方法,不用创建类实例,直接可以使用的属性和方法,伴生对象使用 companion object {} 关键字,然后直接使用类名调用属性或方法:
class Father{
    companion object {
        fun show(){
            println("Hello")
        }
    }
}

fun main(args: Array) {
    Father.show()
}

    结果:


你可能感兴趣的:(Kotlin)