繼承和重寫

kt 中使用 :(冒號) 代替 java 中的 extends 和 implements

重寫父類的方法時,必須使用 override 標識。

當有多個父類,且父類中的方法沖突時,可以通過 把父類放到 super<> 的尖括號中指定調(diào)用哪個父類中的方法。

class Impl:Itf,Itf2{
    override fun test() {
        super<Itf>.test() // 調(diào)用 Itf 接口中的 test 方法
        super<Itf2>.test() // 調(diào)用 Itf 接口中的 test 方法
        super.say()  // 方法不沖突時,直接使用 super. 調(diào)用父類的方法
        println("impl")
    }
}

類的定義

使用 class 關鍵字定義一個類

class Demo2{
    fun test() = println("demo2#test")

    fun test2() = println("demo2#open")
}

kt 中的所有類都是 final ,不能被繼承;所有方法也都是 final 不能被重寫。


open 與 final

  1. kt 中,類、方法默認都是 final。

  2. 可以使用 open 修飾符取消類、方法或?qū)傩缘?final 屬性

  3. 繼承的方法、屬性也是 open,除非手動使用 final 進行修飾:

class Demo

open class Demo2{
    fun test() = println("demo2#test")

    open fun test2() = println("demo2#open")
}

open class Demo3:Demo2(){ // 可以繼成 Demo2,但不能集成 Demo
    final override fun test2() { // 重寫 test2() ,但不能重寫 test
        super.test2()
    }
}

class Demo4:Demo3(){
    // 由于 Demo3 將 test2() 聲明為 final 類型,所以該類中沒有可重寫的方法
}

構(gòu)造函數(shù)

kt 中的構(gòu)造函數(shù)分為 主構(gòu)造函數(shù),從構(gòu)造函數(shù)。主構(gòu)造函數(shù)在類的外部聲明,而從構(gòu)造函數(shù)在類的內(nèi)部聲明。

  1. 所有的從構(gòu)造函數(shù)使用 constructor 聲明。

如果沒有給一個類聲明任何構(gòu)造函數(shù),將會生成一個不做任何事情的默認構(gòu)造函數(shù)。

  1. 同時定義主構(gòu)造函數(shù)與從構(gòu)造函數(shù)時,從構(gòu)造函數(shù)必須調(diào)用到主構(gòu)造函數(shù)。如下,雖然主構(gòu)造函數(shù)沒有做任何操作,但從構(gòu)造函數(shù)最終必須要調(diào)用到主構(gòu)造函數(shù)。

  2. 構(gòu)造函數(shù)通過 this 相互調(diào)用。

class Child2(){
    constructor(a:Int):this(a,1)
    constructor(a:Int,b:Int):this()
}

主構(gòu)造函數(shù)與 init

主構(gòu)造函數(shù)定義在類的外面。

主構(gòu)造函數(shù)功能很單一,只能 為類定義和參數(shù)同名的屬性,并生成其對應的 getter/setter。如果需要在構(gòu)造函數(shù)中執(zhí)行一些初始化操作,需要使用 init 定義初始化語句塊

  1. 初始化語句塊只能與主構(gòu)造函數(shù)一起使用。一個類中可以定義多個初始化語句塊。

  2. init 塊中可以使用主構(gòu)造函數(shù)中的參數(shù)。如下面代碼中,可以在 init 塊中直接使用 name 參數(shù)。

    // 定義主構(gòu)造函數(shù)
    class Demo constructor(name:String){
    val name:String
    init {
    this.name = name
    }
    }

上述代碼如下面的 java 代碼功能一樣,為類定義一個 name 屬性,同時將構(gòu)造函數(shù)中的 name 值賦值給 name 屬性:

class Demo{
    public final String name;
    Demo(String name){
        this.name = name;
    }
}

主構(gòu)造函數(shù)的演化

在不進行簡化的情況下,主構(gòu)造函數(shù)的定義如下:

// 定義構(gòu)造函數(shù),同時對主構(gòu)造函數(shù)進行修飾與使用注解
class Test @A private constructor(name:String){
    lateinit var name_:String
    init {
        this.name_ = name
    }
}
  1. 如果主構(gòu)造函數(shù)沒有被注解或者可見性修飾符修飾時,可以直接使用 constructor。如下:

    class Test constructor(name:String){
        lateinit var name_:String
        init {
            this.name_ = name
        }
    }
    
  2. 當主構(gòu)造函數(shù)只有 constructor 時,可以省略 constructor 關鍵字。因此上面代碼可以簡寫如下:

    class Test(name:String){
        lateinit var name_:String
        init {
            this.name_ = name
        }
    }
    
  3. 如果 init 代碼塊中只是為同名屬性賦值,可以省略 init 代碼塊

    class Test(name:String){
        val name = name // 可以直接使用構(gòu)造函數(shù)中的參數(shù)為屬性賦值
    }
    
  4. 如果不需要為屬性自定義 getter/setter,可以將修飾屬性的 var/val 提到構(gòu)造函數(shù)中。此時會生成默認的 getter/setter

    class Test(val name: String)
    
  5. 主構(gòu)造函數(shù)中的屬性無法直接設置 getter/setter,需要用另外的屬性:

    class Test(name:String,age:Int){
    
        var name = name
            get() {
                println("getter")
                return "from getter"
            }
    }
    

從構(gòu)造函數(shù)

定義在類的內(nèi)部,而不是類名后面的構(gòu)造函數(shù)

大部分情況下,使用主構(gòu)造函數(shù)就夠了。之所以定義從構(gòu)造函數(shù),是為了能通過不同的方式初始化類。

```kotlin
class Demo{
    val name:String
    // 從構(gòu)造函數(shù)
    constructor(name: String){
       this.name = name
    }
}
```

繼承時的構(gòu)造函數(shù)

同 java 一樣,子類的構(gòu)造函數(shù)必須調(diào)用到父類的構(gòu)造函數(shù)。

  1. 唯一原則:確保無論用戶調(diào)用哪個構(gòu)造函數(shù),它必須能調(diào)用到父類的構(gòu)造函數(shù)。

  2. 有主構(gòu)造函數(shù)時,從構(gòu)造函數(shù)不能直接調(diào)用父構(gòu)造函數(shù)。

    open class Parent{
       constructor(name: String)
    }
    
    class Child():Parent("x")
    
    class Child2:Parent{
       constructor():super("xx")
    }
    
    class Child3():Parent(""){
       constructor(name:String):this()
    }
    

    第一個類中,用戶調(diào)用主構(gòu)造函數(shù)時,可以調(diào)用到父類。

    第二個類中,用戶調(diào)用從時,可以調(diào)用到父類。如果定義主構(gòu)造函數(shù),則會報錯。因為用戶調(diào)用主構(gòu)造函數(shù)時,無法調(diào)用到父類的構(gòu)造函數(shù)。

    第三個類中,有主從。從只能調(diào)用主構(gòu)造函數(shù)。這是因為主構(gòu)造函數(shù)可能會定義一系列的變量,如果調(diào)用不到主,則這些變量無法初始化。


抽象類

定義方式與 java 一樣。

  1. 普通類默認是 final ,但抽象類始終是 open

  2. 抽象類中的非抽象函數(shù)默認是 final,抽象函數(shù)是 open。

abstract class Demo2{
    fun test() = println("demo2#test") // 默認是 final,子類不能重寫 test()

    open fun test2() = println("demo2#open") // 顯式使用 open 關鍵字

    abstract fun test3() // 抽象函數(shù),默認是 open。被重寫后,默認也是 open
}

內(nèi)部類與嵌套類

  1. kt 中直接將一個類定義在另一個類內(nèi)部,該類叫嵌套類。嵌套類不持有外部類的引用。相當于 java 中被 static 修飾的內(nèi)部類。

  2. 嵌套類使用 inner 修飾,則該類為內(nèi)部類,此時同普通的 java 內(nèi)部類??梢砸猛獠款惖某蓡T屬性、方法。

  3. 內(nèi)部類通過 this@Outer 獲取外部類的引用

    class Student{
        val name = "name"
    
        override fun toString(): String {
            return "toString"
        }
    
        class Inner{
            fun test() = println("--") // 無法引用外部類
        }
        // 定義成內(nèi)部類
        inner class InnerClass{
            override fun toString(): String {
                return "innerclass"
            }
            fun test() = println("$name ${this@Student.toString()}") // name toString
        }
    }
    

密封類

使用 sealed 修飾符定義的類是密封類,密封類要求 所有直接子類必須定義在父類所在的文件中

  1. sealed 隱含的這個類是 open 類,不需要再顯式地聲明 open。

  2. 密封類的構(gòu)造方法是 private 。

  3. java 中不能繼承密封類。kt 中可以在密封類所在的文件中定義密封類的子類,不是同一文件不繼承。

  4. sealed 不能用于修飾接口

// 密封類
sealed class Student{
    // 內(nèi)部類可以繼承
    private class S : Student()
}
// 同文件中可以繼承
class S1:Student()

數(shù)據(jù)類

使用 data 修飾的類為數(shù)據(jù)類,數(shù)據(jù)類會根據(jù)主構(gòu)造函數(shù)自動重寫能用方法

通用方法一般包括有:equals(),toString(), hashCode()。

  1. equals() 會檢測所有屬性的值是否相等

  2. toString() 生成按聲明順序排列的所有字段的字符串表達形式

  3. hashCode() 會返回一個根據(jù)所有屬性生成的哈希值。

  4. equals ,toString() 和 hashCode() 只會考慮在主構(gòu)造函數(shù)中聲明的屬性

  5. 由于 hashCode 會隨屬性值的變化而變化,因此在用于 map 中作為鍵值時,其 主構(gòu)造函數(shù)中的屬性必須要聲明為 val

    fun main(args:Array<String>){
        val demo = Parent(1,"ls")
        println("hashCode = ${demo.hashCode()} toString = ${demo.toString()}")
    
        val d = Parent(1,"ls",20)
        println("hashCode = ${d.hashCode()} toString = ${d.toString()}")
    
        println(demo == demo) // true
    
        val map = hashMapOf<Parent,String>()
        val key = Parent(1,"ss")
        map.put(key,"000")
        println(map[key]) // 000
        key.name = "xx"
        println(map[key]) // null
    }
    
    data class Parent(var age:Int,var name:String){
        var score = 0
        constructor(age:Int,name: String,score:Int):this(age,name){
            this.score = score
        }
    }
    

    上述代碼中,如果修改其中一個屬性的值, map 返回的結(jié)果為 null。這是因為 hashCode 值已經(jīng)發(fā)生了變化。

  6. data 類在生成時,會自動添加 copy 方法:在調(diào)用 copy() 時修改其些屬性的值。避免無法修改屬性值。這就是原型模式。

    fun main(args:Array<String>){
        val demo = Parent(1,"ls")
    
        val  dc = demo.copy(2,"copy")
        // 兩者的 hashCode 返回結(jié)果不一樣
        println("demo = ${demo.hashCode()} dc = ${dc.hashCode()}")
    }
    

類委托

在使用裝飾模式時經(jīng)常會出現(xiàn)這種情況:除了少部分要重寫的方法外,大部分方法都只是簡單的將請求轉(zhuǎn)發(fā)給被裝飾者。這些轉(zhuǎn)發(fā)請求的代碼就是所謂的樣板代碼 —— 所有的轉(zhuǎn)發(fā)代碼格式都一樣。

kt 中使用 by 關鍵字省略樣板代碼。

如下面代碼,除了 add 方法外,其余的方法都是直接將操作轉(zhuǎn)發(fā)給 innerSet 的同名操作。

// 使用 by 關鍵字常未重寫的操作委托給 innerSet ,而重寫的方法調(diào)用 Count 定義的
class Count<T>(private val innerSet:MutableCollection<T> = HashSet()):MutableCollection<T> by innerSet{
    override fun add(element: T): Boolean {
        println("add ${element}")
        return innerSet.add(element)
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 1 基本信息 每個開發(fā)人員對java.lang.ClassNotFoundExcetpion這個異??隙ǘ疾荒吧?,...
    java小菜鳥閱讀 2,686評論 0 15
  • 本文博客園地址:http://www.cnblogs.com/xiongxuanwen/p/4290086.htm...
    先之閱讀 957評論 0 5
  • 在UML 2.0的13種圖形中,類圖是使用頻率最高的UML圖之一。Martin Fowler在其著作《UML Di...
    雷雷_zll閱讀 13,507評論 0 14
  • 易經(jīng)早課收獲: 1、我們跟隨老師學習指導,就相當于我們登上了船,坐上了車,容易到達彼岸。這個票就是我們對師父的信心...
    le168閱讀 175評論 0 0

友情鏈接更多精彩內(nèi)容