Kotlin 小細(xì)節(jié)記錄(5)

83.Kotlin語言的繼承與重載的open關(guān)鍵字
84.Kotlin語言的類型轉(zhuǎn)換
85.Kotlin語言的智能類型轉(zhuǎn)換
86.Kotlin語言的Any超類
87.Kotlin語言的對象聲明
88.Kotlin語言的對象表達(dá)式
89.Kotlin語言的伴生對象
90.Kotlin語言的嵌套類
91.Kotlin語言的數(shù)據(jù)類
92.Kotlin語言的copy函數(shù)
93.Kotlin語言的解構(gòu)聲明
94-Kotlin語言的運算符重載
95-Kotlin語言的枚舉類
96-Kotlin語言的枚舉類定義函數(shù)
97-Kotlin語言的代數(shù)數(shù)據(jù)類型
98-Kotlin語言的密封類
99-數(shù)據(jù)類使用條件

open 聲明class, is 和 as 關(guān)鍵字 作為檢查和轉(zhuǎn)換操作

kotlin 默認(rèn)創(chuàng)建一個Class情況的class是底層:public final class 拒絕繼承
如果想要可以后續(xù)繼承使用,需要在前面添加open 關(guān)鍵字

open class Parent(var name:String){
   fun show() = println("$name")
}


class Children(var childName:String):Parent(childName){
    fun show2() {
        println("parent:$name,child:$childName")
    }
}

fun main() {
    val p:Parent = Children("zcwfeng")
    println(p.name)
    val c = Children("david")
    if(c is Parent){
        val c2:Parent = c as Parent
        println(c2.show())
    }
}

is 和 as 關(guān)鍵字

安全非空操作

val x: String? = y as? String

Kotlin 在編譯時確保涉及泛型的操作的類型安全,而在運行時,泛型類型的實例不保存有關(guān)其實際類型參數(shù)的信息。
List<Foo> 被刪除為 List<*>。 通常,無法在運行時檢查實例是否屬于具有某些類型參數(shù)的泛型類型。
因此,編譯器禁止由于類型擦除而無法在運行時執(zhí)行的 is-check,例如 ints is List<Int> 或 list is T(類型參數(shù))。 可以根據(jù)星形投影類型檢查實例:【Any?】
koltlin 類型推斷,進(jìn)行了只能轉(zhuǎn)換 【handleStrings】

if (something is List<*>) {
    something.forEach { println(it) } // The items are typed as `Any?`
}

val something = listOf("a","b")
    if(something is List<*>){
        println(something)
    }


fun handleStrings(list: List<String>) {
        if (list is ArrayList) {
            // `list` is smart-cast to `ArrayList<String>`
        }
    }

Reified 需要和inline 內(nèi)聯(lián)函數(shù)一起用

上面說到open,和類型轉(zhuǎn)換提到了泛型轉(zhuǎn)換

【 Reified 允許您在使用泛型來進(jìn)行編程的同時,還能夠在運行時獲取到泛型所代表的類型信息,這在之前是無法做到的。當(dāng)您需要在內(nèi)聯(lián)函數(shù)中使用到類型信息,或者需要重載泛型返回值時,您可以使用 reified。使用 reified 不會帶來任何性能上的損失,但是如果被內(nèi)聯(lián)的函數(shù)過于復(fù)雜則,還是可能會導(dǎo)致性能問題。因為 reified 必須使用內(nèi)聯(lián)函數(shù),所以要保證內(nèi)聯(lián)函數(shù)的簡短,并且遵循使用內(nèi)聯(lián)函數(shù)的最佳實踐,以免讓性能受到損失。】

inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
    if (first !is A || second !is B) return null
    return first as A to second as B
}


    val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)
    val stringToSomething = somePair.asPairOf<String, Any>()
    val stringToInt = somePair.asPairOf<String, Int>()
    val stringToList = somePair.asPairOf<String, List<*>>()
    val stringToStringList = somePair.asPairOf<String, List<String>>()
    println("somePair -> ${somePair}")
    println("stringToSomething -> ${stringToSomething}")
    println("stringToInt -> ${stringToInt}")
    println("stringToList -> ${stringToList}")
    println("stringToStringList -> ${stringToStringList}")

Kt 所有類都隱含繼承了Any(),Any類在Kt中之制定了標(biāo)準(zhǔn),具體實現(xiàn)在各個平臺實現(xiàn)好了

單利實現(xiàn)
object class

object Test1 {
    init {
        println("kt init")
    }

    fun show() = println("ia am show Object class")
}

fun main() {
    println(Test1)
    println(Test1)
    println(Test1)

    println(Test1.show())

}

原理可以借助編譯器 中kt的反編譯工具查看。init----》static代碼塊,show 實現(xiàn)final 實現(xiàn),static 中給public static final Test1 INSTANCE 初始化

接口interface聲明調(diào)用,Kotlin只有一種方式object:接口實現(xiàn)。 java 有兩種方式

open class Test2 {
    open fun add(info:String) = println("kt add:$info")
}

class TestImpl:Test2(){
    override fun add(info: String) {
//        super.add(info)
        println("kt 具名實現(xiàn) add:$info")
    }

}

interface RunnableKt{
    fun runKt()
}

fun main() {
    val p = object : Test2(){
        override fun add(info: String) {
//            super.add(info)
            println("kt 匿名 add:$info")
        }
    }
    p.add("匿名實現(xiàn)")
    val pImpl = TestImpl()
    pImpl.add("張三")
    //Java interface 方式
    val p2 = object:Runnable{
        override fun run() {
            println("Java interface")
        }
    }
    val p3 = Runnable { println("Java interface2") }
    p2.run()
    p3.run()

    //Kt Interface 只有一種
    object:RunnableKt{
        override fun runKt() {
            println("Kt interface")
        }
    }.runKt()

}

伴生生對象,這個點很簡單,Companion看反編譯代碼

class Test3 {
    companion object{
        val info = "demo"
        fun show() = println("compaion $info")
    }
}

fun main() {
    println(Test3.info)
    println(Test3.show())

    //companion 只有一次初始化
    Test3()
    Test3()
    Test3()
}

companion 底層實際,static final class的方式

內(nèi)部類需要inner修飾,嵌套類

class Test4(body:String) {
    val bodyInfo = body

    inner class Hand{
        fun work() = println("Hand is work with: $bodyInfo")

        inner class LeftHand { // 左手
            fun run() = println("左手訪問身體信息:$bodyInfo")
        }

        inner class RightHand { // 右手
            fun run() = println("右手訪問身體信息:$bodyInfo")
        }
    }

    inner class Heart{
        fun work() = println("Hand is work with: $bodyInfo")
    }

    fun show(){
        Hand().work()
    }
}

class Outer{
    val outString = "aaa"
    fun out(){
        Nested().nest()

    }
    class Nested{
        fun nest(){
            println("nest class")
//            Outer().out()
//            out()
//            println(outString)
        }
    }
}

fun main() {
// 內(nèi)部類:
    Test4("isOK").Hand().LeftHand().run()
    Test4("isOK").Hand().RightHand().run()

    // 嵌套類:
    Outer.Nested().nest()
}

嵌套類特點:外部的類 能訪問 內(nèi)部的嵌套類,內(nèi)部的類 不能訪問 外部類的成員
內(nèi)部類的特點: 內(nèi)部的類 能訪問 外部的類,外部的類 能訪問 內(nèi)部的類

data class 數(shù)據(jù)類和普通類
data class 特點:

  1. 內(nèi)部進(jìn)行了component 函數(shù)對字段進(jìn)行解構(gòu)
  2. toString復(fù)寫
  3. copy 克隆函數(shù)
  4. equals 重寫

class Normal(var code:Int,var msg:String,var data:String)
data class DataNormal(var code:Int,var msg:String,var data:String)

fun main() {
    println(Normal(200,"isOk","body"))
    println(DataNormal(200,"isOk","body"))

    println(Normal(200,"isOk","body") == Normal(200,"isOk","body"))//false
    println(DataNormal(200,"isOk","body") == DataNormal(200,"isOk","body"))//true

}

data 的copy,只關(guān)心主構(gòu)造,不管次構(gòu)造

data class DataPerson(var name:String,var age:Int){
    var corinfo = ""
    init {
        println("主構(gòu)造被調(diào)用")
    }
    constructor(name:String):this(name,12){
        println("此構(gòu)造constructer構(gòu)造被調(diào)用")
        corinfo = "增加核心內(nèi)容"

    }

    override fun toString(): String {
        return "toString name:$name, age:$age, coreInfo:$corinfo"
    }
}

調(diào)用:
   val p1 = DataPerson("zhangsan")
    val p2 = p1.copy("zcwfeng",100)
    println(p1)
    println(p2)
--------
主構(gòu)造被調(diào)用
此構(gòu)造constructer構(gòu)造被調(diào)用
主構(gòu)造被調(diào)用
toString name:zhangsan, age:12, coreInfo:增加核心內(nèi)容
toString name:zcwfeng, age:100, coreInfo:

data class 解構(gòu)和普通class結(jié)構(gòu)對比

class Student(var name:String,var age:Int){
     operator fun component1():String = name
     operator fun component2():Int = age
}
data class Student2Data(var name:String,var age:Int)


fun main() {
   
    val (name,age) = Student("test",1111)
    println("解構(gòu):name=$name,age=$age")
    val (name1,age1) = Student2Data("test",1111)
    println("解構(gòu)2:name=$name1,age=$age1")
}

必須加入操作符標(biāo)記,否則復(fù)發(fā)接受自定義結(jié)構(gòu) operator fun component1():String = name

目的對比data class 解釋解構(gòu)的特性

操作符重栽,聯(lián)想c++

data class AddPlus(var num1:Int,var num2:Int){
    operator fun plus(p:AddPlus): Int {
        return (p.num1 +num1 + p.num2 + num2)
    }
    //AddPlus. 可以提示你可重新載入的操作符
    operator fun div(p:AddPlus):AddPlus{
        return AddPlus(num1+p.num1,num2 + p.num2)
    }
}

fun main() {
    println(AddPlus(1,1) + AddPlus(2,2))
//    val (a,b) = AddPlus(1,1) / AddPlus(2,2)
//    println("a,b,$a,$b")
    println(AddPlus(1,1) / AddPlus(2,2))
}

利用編譯器提示可用的操作符,AddPlus 加點就可以
用data class是為了輸出方便查看,直接class也是可以的

枚舉類的使用和經(jīng)驗
簡單項目中狀態(tài)

enum class UIStateEnum constructor(v: Int) {
    //1,取消搜藏,2,搜藏,3,取消點贊,4,點贊,5 取消關(guān)注,6 關(guān)注,7,取消關(guān)注番劇 8,關(guān)注追番劇
    Undefined(0),
    CancelCollect(1),
    CollectState(2),
    UnLikeState(3),
    LikeState(4),
    UnFollowUser(5),
    FollowUser(6),
    UnFollowVideo(7),
    FollowVideo(8);

    var value = v
    override fun toString(): String {
        return "$value"
    }
}

    println(UIStateEnum.Undefined.value)
    println(UIStateEnum.Undefined.toString())

簡單封裝一下使用

data class Info(var libinfo:String,var length:Int)

enum class LibInfo(var info: Info){
    L_HAND(Info("左手", 88)), // 左手
    R_HAND(Info("右手", 88)), // 右手

    L_FOOT(Info("左腳", 140)), // 左腳
    R_FOOT(Info("右腳", 140)) // 右腳
    ;

    fun show() = "四肢是:${info.libinfo}的長度是:${info.length}"

    fun updateData(info: Info) {
        println("更新前的數(shù)據(jù)是:${this.info}")
        this.info.libinfo = info.libinfo
        this.info.length = info.length
        println("更新后的數(shù)據(jù)是:${this.info}")
    }

}

// 一般的用法如下:
    println(LibInfo.L_HAND.show())
    println(LibInfo.R_HAND.show())
    println(LibInfo.L_FOOT.show())
    println(LibInfo.R_FOOT.show())

    println()

    // 更新枚舉值
    LibInfo.R_HAND.updateData(Info("====右手22222222", 11))
    LibInfo.L_HAND.updateData(Info("====左手22222222", 11))
    LibInfo.L_FOOT.updateData(Info("====左腳222222", 199))
    LibInfo.R_FOOT.updateData(Info("====右叫222222", 199))

密封類sealed class 使用
舉個我們九年義務(wù)教育和各種教育都用的例子。考試黨委,我們只輸出優(yōu)秀學(xué)生的名字。

sealed class Exams{
    object Fun1:Exams()
    object Fun2:Exams()
    class Fun3(val name:String):Exams()
}

class Teachers(var exam:Exams){
    fun show() =
        when (exam) {
            is Exams.Fun1 -> "及格"
            is Exams.Fun2 -> "良好"
            is Exams.Fun3 -> "優(yōu)秀,學(xué)生名字是${(this.exam as Exams.Fun3).name}"
        }
}

fun main() {
    println(Teachers(Exams.Fun1).show())
    println(Teachers(Exams.Fun2).show())
    println(Teachers(Exams.Fun3("zhang san")).show())
    println(Teachers(Exams.Fun3("wang wu")).show())

    println(Exams.Fun1 === Exams.Fun1) // true, === 必須對象引用, object是單例 只會實例化一次

    println(Exams.Fun3("AAA") === Exams.Fun3("AAA")) // class 有兩個不同的對象,所以是false
}

這個有點想我們的枚舉,使用枚舉可以但是比較麻煩和難做。相似點都是代數(shù)數(shù)據(jù)模型。

解釋一下

1 限制枚舉每個類型只允許有一個實例
2 限制所有枚舉常量使用相同的類型的值
3 Sealed Classes 包含了抽象類和枚舉的優(yōu)勢:抽象類表示的靈活性和枚舉常量的受限性
4 Sealed Classes 用于表示受限制的類層次結(jié)構(gòu),從某種意義上說,Sealed Classes 是枚舉類的擴(kuò)展
5 枚舉的不同之處在于,每個枚舉常量僅作為單個實例存在,而 Sealed Classes 的子類可以表示不同狀態(tài)的實例

Sealed Classes 用于表示層級關(guān)系-->子類可以是任意的類, 數(shù)據(jù)類、Kotlin 對象、普通的類,甚至也可以是另一個 Sealed
Sealed Classes 受限制----> 必須在同一文件中,或者在 Sealed Classes 類的內(nèi)部中使用,在Kotlin 1.1 之前,規(guī)則更加嚴(yán)格,子類只能在 Sealed Classes 類的內(nèi)部中使用

這里說的不是十分準(zhǔn)確,能夠解釋為什么用Sealed比枚舉合適

data class使用場景

  1. 數(shù)據(jù)請求返回bean。
  2. 不允許sealed,abstract,inner,open的修飾
  3. 至少有一個主構(gòu)造參數(shù),并且必須用var,val修飾的
  4. 需要比較,copy,解構(gòu),toString方便的時候可以使用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

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

  • 不重要的廢話 前段時間看了一遍《Programming Kotlin》,主要目的是想提高自己的英文閱讀能力,能力提...
    珞澤珈群閱讀 3,575評論 1 7
  • 1. 重點理解val的使用規(guī)則 引用1 如果說var代表了varible(變量),那么val可看成value(值)...
    leeeyou閱讀 570評論 0 0
  • 1. 變量常量與類型 聲明變量 常用類型 變量類型 例: 查看字節(jié)碼 雙擊Shift --> 輸入 show k...
    打工崽閱讀 410評論 0 2
  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,686評論 9 118
  • 一、Kotlin基礎(chǔ) 1.1 變量 在Kotlin中變量分為可變引用var和不可變引用val,val對應(yīng)的是jav...
    東方未曦閱讀 600評論 0 1

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