Kotlin学习历程——继承

Kotlin语言中文站

在Kotlin中所有类都有一个共同的父类Any,这对于没有父类声明的类是默认父类。这跟Java是类似的,只不过在Java中默认父类是Object

这里我们看看Any类源码:

public open class Any {
    
    
    /**
     * Indicates whether some other object is "equal to" this one. Implementations must fulfil the following
     * requirements:
     *
     * * Reflexive: for any non-null value `x`, `x.equals(x)` should return true.
     * * Symmetric: for any non-null values `x` and `y`, `x.equals(y)` should return true if and only if `y.equals(x)` returns true.
     * * Transitive:  for any non-null values `x`, `y`, and `z`, if `x.equals(y)` returns true and `y.equals(z)` returns true, then `x.equals(z)` should return true.
     * * Consistent:  for any non-null values `x` and `y`, multiple invocations of `x.equals(y)` consistently return true or consistently return false, provided no information used in `equals` comparisons on the objects is modified.
     * * Never equal to null: for any non-null value `x`, `x.equals(null)` should return false.
     *
     * Read more about [equality](https://kotlinlang.org/docs/reference/equality.html) in Kotlin.
     */
    public open operator fun equals(other: Any?): Boolean

    /**
     * Returns a hash code value for the object.  The general contract of `hashCode` is:
     *
     * * Whenever it is invoked on the same object more than once, the `hashCode` method must consistently return the same integer, provided no information used in `equals` comparisons on the object is modified.
     * * If two objects are equal according to the `equals()` method, then calling the `hashCode` method on each of the two objects must produce the same integer result.
     */
    public open fun hashCode(): Int

    /**
     * Returns a string representation of the object.
     */
    public open fun toString(): String
}

由上可知,也就为所有kotlin类都定义了这些方法。

Kotlin类默认是最终(final)的:不能被继承。如果需要被继承,请用关键字open:

class Shape() //不可被继承(默认)

open class Shape() //可被继承

如需继承父类,请在类头中把父类放到冒号之后:

open class Circle: Shape()

如果子类有一个主构造函数,其父类必须用子类主构造函数的参数“就地”初始化,有点抽象,看代码就明白了:

//父类
open class Shape(name:String){
    
    }

//子类, “就地”初始化
open class Circle(name:String): Shape(name){
    
    }

如果子类没有主构造函数呢?那就用带参数的次构造函数,父类总得初始化吧:

open class Circle: Shape {
    
    
   constructor(name: String) : super(name) {
    
    
   }

   //委托给上面的构造函数
   constructor(name: String, color: String) : this(name) {
    
    
   }  
}

如上,每个次构造函数必须用关键字super初始化父类,或委托给另外一个构造函数做到这一点。 当然,不同的次构造函数具体委托父类哪个构造方法,就看我们自己决定了:

open class Shape(name:String) {
    
    
   constructor(name:String, color: String) : this(name) {
    
    
   } 
}

//子类
open class Circle: Shape {
    
    
    constructor(name: String) : super(name) {
    
    
    }

    //这里我也可以委托父类主构造函数 super(name),但是这样color参数就没意义了
    constructor(name: String, color: String) : super(name, color) {
    
    
    }
}

覆盖方法

一个方法如果想被子类覆盖,仍然需要使用关键字open

open class Shape(name:String) {
    
    
    constructor(name:String, color: String) : this(name) {
    
    

    }
    //可被子类覆盖
    open fun name() {
    
    
        print("fun##name.")
    }
    //子类不可覆盖
    fun draw(){
    
    }
}


//子类
open class Circle: Shape {
    
    
    ...
    
    override fun name() {
    
    
        super.name()
    } 
    ...
}

如上,name()函数被覆盖,同时它也可以继续被Circle的子类覆盖(相当于加了open)。如果想禁止被子类覆盖,可以使用final关键字。

...
final override fun name() {
    
    
     super.name()
}
...

覆盖属性

属性覆盖跟方法覆盖类似,均需要使用open关键字修饰。

open class Shape(name:String) {
    
    
    //属性
    open val tempAttribute : String = ""

    constructor(name:String, color: String) : this(name) {
    
    

    }
    
    open fun name() {
    
    
        print("fun##name.")
    }

    fun draw(){
    
    }
}



//子类
open class Circle: Shape {
    
    
    //覆盖属性
    override val tempAttribute: String = "Circle"
    
    constructor(name: String) : super(name) {
    
    

    }

    //我也可以委托父类主构造函数 super(name), 和上面的函数一样
    constructor(name: String, color: String) : super(name, color) {
    
    

    }

    final override fun name() {
    
    
        super.name()

    }
}


//子类覆盖属性还有种简单的写法,直接在主构造函数进行覆盖
open class Circle(name:String, override val tempAttribute: String): Shape(name) {
    
    
    final override fun name() {
    
    
        super.name()
    }
}

这里需要备注说明一下,我们可以使用var属性覆盖一个val属性,但反之不行。 你如果强行这么做,IDE也不允许,直接报错:


调用父类的方法

子类调用父类的函数或者属性访问器的实现可以使用super关键字:

open class Circle(name:String): Shape(name) {
    
    
    val childTempAttr: String = super.tempAttribute

    override fun name() {
    
    
        super.name()
    }
}

有两种使用场景需要注意下:

  • 场景一: 在内部类访问外部类的父类成员方法,可以通过由外部类名限定的super关键字来实现:
open class Circle(name:String): Shape(name) {
    
    

    val childTempAttr: String = super.tempAttribute

    override fun name() {
    
    
        super.name()
    }

    //内部类
    inner class Ball {
    
    
        fun draw() {
    
    
            //直接调用外部类的方法
            name()

            //调用外部类父类的方法
            super@Circle.name();
        }
    }
}

挺怪异的写法,我们把代码转成Java看看:

...
public final class Ball {
    
    
  public final void draw() {
    
    
     Circle.this.name();
     Circle.super.name();
  }
}
...

是不是有种“拨开云雾见天日”的感觉…

  • 场景二: 一个类继承了一个类同时实现了一个接口,并且两个父类都有相同的方法,子类如何调用父类的这个方法,如何区分呢?
//父类1
open class Rectangle {
    
    
    open fun draw() {
    
    
    }
}

//父类2
interface Polygon {
    
    
    fun draw(){
    
    
    }
}

//子类
class Square : Rectangle(), Polygon {
    
    
    override fun draw() {
    
    
        //调用Rectangle
        super<Rectangle>.draw()
        
        //调用Polygon
        super<Polygon>.draw()
		
		//子类也可以不调用父类,自己实现自己的逻辑
		/** todo */
    }
}

子类初始化顺序

如下代码,打印的结果是啥?

//父类
open class Shape(name:String) {
    
    

    open val tempAttribute : String = "Shape##tempAttribute".also {
    
     println(it) }

    init {
    
    
        println("Shape##init.")
    }

    constructor(name:String, color: String) : this(name) {
    
    
        println("Shape##constructor")
    }
}

//子类
class Square : Shape {
    
    

    override val tempAttribute: String = "Square##tempAttribute".also {
    
     println(it) }

    init {
    
    
        println("Square##init")
    }

    constructor(name: String, color: String) : super(name, color) {
    
    
        println("Square##constructor")
    }
}

//执行
fun main(args: Array<String>) {
    
    
    Square("正方形","红色")
}

//打印结果
Shape##tempAttribute
Shape##init.
Shape##constructor

Square##tempAttribute
Square##init
Square##constructor

所以父类的初始化是最先执行的,接着才是子类的初始化,跟Java别无二致。


上一篇:Kotlin学习历程——类的定义
下一篇:Kotlin学习历程——接口

猜你喜欢

转载自blog.csdn.net/hepingdev/article/details/124073753