Kotlin 小细节记录(5)

83.Kotlin语言的继承与重载的open关键字
84.Kotlin语言的类型转换
85.Kotlin语言的智能类型转换
86.Kotlin语言的Any超类
87.Kotlin语言的对象声明
88.Kotlin语言的对象表达式
89.Kotlin语言的伴生对象
90.Kotlin语言的嵌套类
91.Kotlin语言的数据类
92.Kotlin语言的copy函数
93.Kotlin语言的解构声明
94-Kotlin语言的运算符重载
95-Kotlin语言的枚举类
96-Kotlin语言的枚举类定义函数
97-Kotlin语言的代数数据类型
98-Kotlin语言的密封类
99-数据类使用条件

open 声明class, is 和 as 关键字 作为检查和转换操作

kotlin 默认创建一个Class情况的class是底层:public final class 拒绝继承
如果想要可以后续继承使用,需要在前面添加open 关键字

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 关键字

安全非空操作

val x: String? = y as? String

Kotlin 在编译时确保涉及泛型的操作的类型安全,而在运行时,泛型类型的实例不保存有关其实际类型参数的信息。
List<Foo> 被删除为 List<*>。 通常,无法在运行时检查实例是否属于具有某些类型参数的泛型类型。
因此,编译器禁止由于类型擦除而无法在运行时执行的 is-check,例如 ints is List<Int> 或 list is T(类型参数)。 可以根据星形投影类型检查实例:【Any?】
koltlin 类型推断,进行了只能转换 【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 内联函数一起用

上面说到open,和类型转换提到了泛型转换

【 Reified 允许您在使用泛型来进行编程的同时,还能够在运行时获取到泛型所代表的类型信息,这在之前是无法做到的。当您需要在内联函数中使用到类型信息,或者需要重载泛型返回值时,您可以使用 reified。使用 reified 不会带来任何性能上的损失,但是如果被内联的函数过于复杂则,还是可能会导致性能问题。因为 reified 必须使用内联函数,所以要保证内联函数的简短,并且遵循使用内联函数的最佳实践,以免让性能受到损失。】

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中之制定了标准,具体实现在各个平台实现好了

单利实现
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 实现final 实现,static 中给public static final Test1 INSTANCE 初始化

接口interface声明调用,Kotlin只有一种方式object:接口实现。 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 具名实现 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("匿名实现")
    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的方式

内部类需要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() {
// 内部类:
    Test4("isOK").Hand().LeftHand().run()
    Test4("isOK").Hand().RightHand().run()

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

嵌套类特点:外部的类 能访问 内部的嵌套类,内部的类 不能访问 外部类的成员
内部类的特点: 内部的类 能访问 外部的类,外部的类 能访问 内部的类

data class 数据类和普通类
data class 特点:

  1. 内部进行了component 函数对字段进行解构
  2. toString复写
  3. copy 克隆函数
  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,只关心主构造,不管次构造

data class DataPerson(var name:String,var age:Int){
    var corinfo = ""
    init {
        println("主构造被调用")
    }
    constructor(name:String):this(name,12){
        println("此构造constructer构造被调用")
        corinfo = "增加核心内容"

    }

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

调用:
   val p1 = DataPerson("zhangsan")
    val p2 = p1.copy("zcwfeng",100)
    println(p1)
    println(p2)
--------
主构造被调用
此构造constructer构造被调用
主构造被调用
toString name:zhangsan, age:12, coreInfo:增加核心内容
toString name:zcwfeng, age:100, coreInfo:

data class 解构和普通class结构对比

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("解构:name=$name,age=$age")
    val (name1,age1) = Student2Data("test",1111)
    println("解构2:name=$name1,age=$age1")
}

必须加入操作符标记,否则复发接受自定义结构 operator fun component1():String = name

目的对比data class 解释解构的特性

操作符重栽,联想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也是可以的

枚举类的使用和经验
简单项目中状态

enum class UIStateEnum constructor(v: Int) {
    //1,取消搜藏,2,搜藏,3,取消点赞,4,点赞,5 取消关注,6 关注,7,取消关注番剧 8,关注追番剧
    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("更新前的数据是:${this.info}")
        this.info.libinfo = info.libinfo
        this.info.length = info.length
        println("更新后的数据是:${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 使用
举个我们九年义务教育和各种教育都用的例子。考试党委,我们只输出优秀学生的名字。

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 -> "优秀,学生名字是${(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
}

这个有点想我们的枚举,使用枚举可以但是比较麻烦和难做。相似点都是代数数据模型。

解释一下

1 限制枚举每个类型只允许有一个实例
2 限制所有枚举常量使用相同的类型的值
3 Sealed Classes 包含了抽象类和枚举的优势:抽象类表示的灵活性和枚举常量的受限性
4 Sealed Classes 用于表示受限制的类层次结构,从某种意义上说,Sealed Classes 是枚举类的扩展
5 枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例

Sealed Classes 用于表示层级关系-->子类可以是任意的类, 数据类、Kotlin 对象、普通的类,甚至也可以是另一个 Sealed
Sealed Classes 受限制----> 必须在同一文件中,或者在 Sealed Classes 类的内部中使用,在Kotlin 1.1 之前,规则更加严格,子类只能在 Sealed Classes 类的内部中使用

这里说的不是十分准确,能够解释为什么用Sealed比枚举合适

data class使用场景

  1. 数据请求返回bean。
  2. 不允许sealed,abstract,inner,open的修饰
  3. 至少有一个主构造参数,并且必须用var,val修饰的
  4. 需要比较,copy,解构,toString方便的时候可以使用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容