60、Kotlin的主构造函数 

// 主构造函数:规范来说,都是增加_xxx的方式,临时的输入类型,不能直接用,需要接收下来 成为变量才能用
// _name 等等,都是临时的类型,不能直接要弄,需要转化一下才能用
class KtBase60(_name: String, _sex: Char, _age: Int, _info: String) // 主构造函数
{
    var name = _name
        get() = field // get不允许私有化
        private set(value) {
            field = value
        }

    val sex = _sex
        get() = field
        // set(value) {} 只读的,不能修改的,不能set函数定义

    val age: Int = _age
        get() = field + 1

    val info = _info
        get() = "【${field}】"

    fun show() {
        // println(_name) 临时的输入类型,不能直接用,需要接收下来 成为变量才能用
        println(name)
        println(sex)
        println(age)
        println(info)
    }
}

fun main() {
    // KtBase60()

    val p = KtBase60(_name = "Zhangsan", _info = "学习KT语言", _age = 88, _sex = 'M')
    // println(p.name)
    // p.name = "AAA" 被私有化了,不能调用

    p.show()
}

61、Kotlin的主构造函数里定义属性

// var name: String  就相当于  var name = _name  这不过你看不到而已
// 一步到位,不像我们上一篇是分开写的
class KtBase61 (var name: String, val sex: Char, val age: Int, var info: String)
{
    fun show() {
        println(name)
        println(sex)
        println(age)
        println(info)
    }
}

fun main() {
    KtBase61(name = "Zhangsan", info = "学习KT语言", age = 88, sex = 'M').show()
}

62、Kotlin的次构造函数

class KtBase62(name: String) // 主构造
{
    // 2个参数的次构造函数,必须要调用主构造函数,否则不通过,  为什么次构造必须调用主构造?答:主构造统一管理 为了更好的初始化设计
    constructor(name: String, sex: Char) : this(name) {
        println("2个参数的次构造函数 name:$name, sex:$sex")
    }

    // 3个参数的次构造函数,必须要调用主构造函数
    constructor(name: String, sex: Char, age: Int) : this(name) {
        println("3个参数的次构造函数 name:$name, sex:$sex, age:$age")
    }

    // 4个参数的次构造函数,必须要调用主构造函数
    constructor(name: String, sex: Char, age: Int, info: String) : this(name) {
        println("4个参数的次构造函数 name:$name, sex:$sex, age:$age, info:$info")
    }
}

// name: String, sex: Char, age: Int, info: String
fun main() {
    val p = KtBase62("李元霸") // 调用主构造

    KtBase62("张三", '男') // 调用 2个参数的次构造函数

    KtBase62("张三2", '男', 88) // 调用 3个参数的次构造函数

    KtBase62("张三3", '男', 78, "还在学习新语言") // 调用 4个参数的次构造函数
}

63、Kotlin的构造函数中默认参数

class KtBase63(name: String = "李元霸") // 主构造
{
    // 2个参数的次构造函数,必须要调用主构造函数
    constructor(name: String = "李连杰", sex: Char = 'M') : this(name) {
        println("2个参数的次构造函数 name:$name, sex:$sex")
    }

    // 3个参数的次构造函数,必须要调用主构造函数
    constructor(name: String = "李小龙", sex: Char = 'M', age: Int = 33) : this(name) {
        println("3个参数的次构造函数 name:$name, sex:$sex, age:$age")
    }

    // 4个参数的次构造函数,必须要调用主构造函数
    constructor(name: String = "李俊", sex: Char = 'W', age: Int = 87, info: String = "还在学习新开发语言") : this(name) {
        println("4个参数的次构造函数 name:$name, sex:$sex, age:$age, info:$info")
    }
}

fun main() {
    val p = KtBase63("李元霸2") // 调用主构造

    KtBase63("张三", '男') // 调用 2个参数的次构造函数

    KtBase63("张三2", '男', 88) // 调用 3个参数的次构造函数

    KtBase63("张三3", '男', 78, "还在学习新语言") // 调用 4个参数的次构造函数

    KtBase63() // 到底是调用哪一个 构造函数,是次构造 还是 主构造 ? 答:优先调用主构造函数
}

64、Kotlin的初始化块

// username: String, userage: Int, usersex: Char  临时类型,必须要二次转换,才能用
class KtBase64 (username: String, userage: Int, usersex: Char) // 主构造
{
    // 这个不是Java的 static{}
    // 相当于是Java的 {} 构造代码块
    // 初始化块  init代码块
    init {
        println("主构造函数被调用了 $username, $userage, $usersex")

        // 如果第一个参数是false,就会调用第二个参数的lambda
        // 判断name是不是空值 isNotBlank   ""
        require(username.isNotBlank()) { "你的username空空如也,异常抛出" }

        require(userage > 0) { "你的userage年龄不符合,异常抛出" }

        require( usersex == '男' || usersex == '女') { "你的性别很奇怪了,异常抛出" }
    }

    constructor(username: String) : this(username, 87, '男') {
        println("次构造函数被调用了")
    }

    fun show() {
        // println(username) // 用不了,必须要二次转换,才能用
    }
}


// 1.name,age,sex的主构造函数
// 2.init代码块学习 require
// 3.临时类型只有在 init代码块才能调用
fun main() {
    KtBase64 ("李四", userage = 88, usersex = '女')  // 调用主构造
    println()
    KtBase64 ("王五") // 调用次构造

    // KtBase64 ("") // 调用次构造

    // KtBase64 ("李四", userage = -1, usersex = 'M')  // 调用主构造

    KtBase64 ("李四", userage = 1, usersex = '男')  // 调用主构造
}

65、Kotlin的构造初始化顺序

// 第一步:生成val sex: Char
class KtBase65(_name: String, val sex: Char) // 主构造
{

    // 第二步: 生成val mName  // 由于你是写在 init代码块前面,所以先生成你, 其实类成员 和 init代码块 是同时生成
    val mName = _name

    init {
        val nameValue = _name // 第三步:生成nameValue细节
        println("init代码块打印:nameValue:$nameValue")
    }

    // 次构造 三个参数的  必须调用主构造
    constructor(name: String, sex: Char, age: Int) :this(name, sex) {
        // 第五步:生成次构造的细节
        println("次构造 三个参数的, name:$name, sex:$sex, age:$age")
    }

    // 第四步
    val Merry = "AAA"

    // 纠正网上优秀博客的错误: 类成员先初始生成   再init代码块初始生成  错误了
    // Merry 正确说法:init代码块 和 类成员 是同时的,只不过你写在 init代码块前面 就是先生成你
}


// 1.main 调用次构造 name sex age
// 2.主构造的 val 变量
// 3.var mName = _name
// 4.init { nameValue 打印 }
fun main() {
    // 调用次构造
    KtBase65("李元霸", '男', 88)  // 调用次构造
}

66、Kotlin的延迟初始化lateinit

 ::变量名.isInitialized  判断变量是否初始化

class KtBase66 {

    // lateinit val AAA; // AAA 无法后面在修改了,我还怎么延时初始化?
    lateinit var responseResultInfo: String // 我等会儿再来初始化你,我先定义再说,所以没有赋值

    // 模拟服务器加载
    fun loadRequest() { // 延时初始化,属于懒加载,用到你在给你加载
        responseResultInfo = "服务器加载成功,恭喜你"
    }

    fun showResponseResult() {
        // 由于你没有给他初始化,所以只有用到它,就奔溃
        // if (responseResultInfo == null) println()
        // println("responseResultInfo:$responseResultInfo")

        if (::responseResultInfo.isInitialized) {
            println("responseResultInfo:$responseResultInfo")
        } else {
            println("你都没有初始化加载,你是不是忘记加载了")
        }
    }
}

// 1.lateinit responseResultInfo 定义
// 2.request 懒加载
// 3.showResponseResult
// 4.main 先请求 在显示
fun main() {
    val p = KtBase66()

    // 使用他之前,加载一下(用到它才加载,就属于,懒加载)
    p.loadRequest()

    // 使用他
    p.showResponseResult()
}

67、Kotlin的惰性初始化by lazy

变量使用的时候才会加载

class KtBase67 {

    // >>>>>>>>>>>>>>>>>>> 下面是 不使用惰性初始化 by lazy  普通方式(饿汉式 没有任何懒加载的特点)
    // val databaseData1 = readSQlServerDatabaseAction()

    // >>>>>>>>>>>>>>>>>>> 使用惰性初始化 by lazy  普通方式
    val databaseData2 by lazy { readSQlServerDatabaseAction() }

    private fun readSQlServerDatabaseAction(): String {
        println("开始读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("加载读取数据库数据中....")
        println("结束读取数据库数据中....")
        return "database data load success ok."
    }

}

// TODO 79.Kotlin语言的惰性初始化by lazy学习
// 1.不使用惰性初始化 databaseData1 = readSQLServerDatabaseAction()
// 2.使用惰性初始化  databaseData2 by lazy
// 3.KtBase82()  睡眠  db1.databaseData1

// lateinit 是在使用的时候,手动加载的懒加载方式,然后再使用
// 惰性初始化by lazy  是在使用的时候,自动加载的懒加载方式,然后再使用
fun main() {
    // >>>>>>>>>>>>>>>>>>> 下面是 不使用惰性初始化 by lazy  普通方式(饿汉式 没有任何懒加载的特点)
    /*val p = KtBase67()

    Thread.sleep(5000)

    println("即将开始使用")

    println("最终显示:${p.databaseData1}")*/


    // >>>>>>>>>>>>>>>>>>> 使用惰性初始化 by lazy  普通方式
    val p = KtBase67()

    Thread.sleep(5000)

    println("即将开始使用")

    println("最终显示:${p.databaseData2}")

}

68、Kotlin的继承与重载的open关键字

// KT所有的类,默认是final修饰的,不能被继承,和Java相反
// open:移除final修饰
open class Person(private val name: String) {

    private fun showName() = "父类 的姓名是【$name】"

    // KT所有的函数,默认是final修饰的,不能被重写,和Java相反
    open fun myPrintln() = println(showName())
}

class Student(private val subName: String) : Person(subName) {

    private fun showName() = "子类 的姓名是【${subName}】"

    override fun myPrintln() = println(showName())

}

// 1.父类 val name  showName()->String  myPrintln->Unit
// 2.子类 myPrintln->Unit
fun main() {
    val person: Person = Student("张三")
    person.myPrintln()
}

69、Kotlin语言的类型转换 (is  /  is+as)

open class Person2(private val name: String) {

    fun showName() = "父类 的姓名是【$name】"

    // KT所有的函数,默认是final修饰的,不能被重写,和Java相反
    open fun myPrintln() = println(showName())
}

class Student2(private val subName: String) : Person2(subName) {

    fun showName2() = "子类 的姓名是【${subName}】"

    override fun myPrintln() = println(showName2())

}

// 1.普通运行子类输出
// 2.is Person Student File
// 3.is + as 转换
fun main() {
    val p: Person2 = Student2("王五")
    p.myPrintln()

    println(p is Person2)
    println(p is Student2)
    println(p is File)

    // is + as = 一般是配合一起使用
    if (p is Student2) {
        (p as Student2).myPrintln()
    }

    if (p is Person2) {
        // (p as Person2).myPrintln() // 因为子类重写了父类
        println((p as Person2).showName())
    }
}

70、Kotlin的Any超类

  • 在KT中,所有的类,都隐士继承了 : Any() ,你不写,默认就有
  • Any类在KT设计中:只提供标准,你看不到实现,实现在各个平台处理好了

71、Kotlin的对象声明 

  •    object KtBase71 既是单例的实例,也是类名
  •     小结:既然是 单例的实例,又是类名,只有一个创建,这就是典型的单例
object KtBase71 {

    init {
        println("KtBase71 init...")
    }

    fun show() = println("我是show函数...")
}

fun main() {
    // object KtBase71 既是单例的实例,也是类名
    // 小结:既然是 单例的实例,又是类名,只有一个创建,这就是典型的单例
    println(KtBase71 ) // 背后代码:println(KtBase71 .INSTANCE)
    println(KtBase71 ) // 背后代码:println(KtBase71 .INSTANCE)

    // 背后代码:KtBase71 .INSTANCE.show();
    println(KtBase71 .show())
}

72、Kotlin的对象表达式

小结:Java接口,有两种方式 1(object : 对象表达式)  2简洁版,
           KT接口,只有一种方式 1(object : 对象表达式)

interface RunnableKT {
    fun run()
}

open class KtBase72 {

    open fun add(info: String) = println("KtBase72 add:$info")

    open fun del(info: String) = println("KtBase72 del:$info")
}

// 1.add del println
// 2.匿名对象表达式方式
// 3.具名实现方式
// 4.对Java的接口 用对象表达式方式
fun main() {
    // 匿名对象 表达式方式
    val p : KtBase72 = object : KtBase72 () {

        override fun add(info: String) {
            // super.add(info)
            println("我是匿名对象 add:$info")
        }

        override fun del(info: String) {
            // super.del(info)
            println("我是匿名对象 del:$info")
        }
    }
    p.add("李元霸")
    p.del("李连杰")


    // 具名实现方式
    val p2 = KtBase72Impl()
    p2.add("刘一")
    p2.del("刘二")

    // 对Java的接口 用   KT[对象表达式方式]  方式一
    val p3 = object : Runnable {
        override fun run() {
            println("Runnable run ...")
        }
    }
    p3.run()

    // 对Java的接口 用   Java最简洁的方式 方式二
    val p4 = Runnable {
        println("Runnable run2 ...")
    }
    p4.run()

    // 对KT的接口 用   KT[对象表达式方式]  方式一
    object : RunnableKT {
        override fun run() {
            println("RunnableKT 方式一 run ...")
        }
    }.run()

    // 对KT的接口 用   Java最简洁的方式 方式二
    /*RunnableKT {

    }*/
}

// 小结:Java接口,有两种方式 1(object : 对象表达式)  2简洁版,
//       KT接口,只有一种方式 1(object : 对象表达式)

// 具名实现  具体名字 == KtBase72Impl
class KtBase72Impl : KtBase72() {

    override fun add(info: String) {
        // super.add(info)
        println("我是具名对象 add:$info")
    }

    override fun del(info: String) {
        // super.del(info)
        println("我是具名对象 del:$info")
    }
}

73、Kotlin的伴生对象 companion object{} 

伴生对象的由来: 在KT中是没有Java的这种static静态,伴生很大程度上和Java的这种static静态 差不多的

class KtBase73 {

    // 伴生对象
    companion object {
        val info = "MerryINfo"

        fun showInfo() = println("显示:$info")

        val name = "Merry"
    }
}

// 伴生对象的由来: 在KT中是没有Java的这种static静态,伴生很大程度上和Java的这种static静态 差不多的
// 无论 KtBase73() 构建对象多少次,我们的伴生对象,只有一次加载
// 无论 KtBase73.showInfo() 调用多少次,我们的伴生对象,只有一次加载
// 伴生对象只会初始化一次
fun main() {
    // 背后代码:System.out.println(KtBase73.Companion.getInfo())
    println(KtBase73.info)

    // 背后代码:System.out.println(KtBase73.Companion.getName())
    println(KtBase73.name)

    // 背后代码:KtBase73.Companion.showInfo()
    KtBase73.showInfo()

    // new KtBase73();
    KtBase73()
    KtBase73()
    KtBase73()
    KtBase73()
    // ...
}

74、kotlin的嵌套类

(1)内部类的特点: 内部的类 能访问 外部的类
                                   外部的类 能访问 内部的类

(2)默认情况下:就是嵌套类关系
 嵌套类特点:外部的类 能访问 内部的嵌套类
                      内部的类 不能访问 外部类的成员

class Body(_bodyInfo: String) { // 身体类

    val bodyInfo = _bodyInfo

    fun show() {
        Heart().run()
    }

    // 默认情况下:内部的类 不能访问 外部的类,要增加修饰符inner 成为内部类才可以访问外部类
    inner class Heart { // 心脏类
        fun run() = println("心脏访问身体信息:$bodyInfo")
    }

    inner class Kidney { // 肾脏
        fun work() = println("肾脏访问身体信息:$bodyInfo")
    }

    inner class Hand { // 手
        inner class LeftHand { // 左手
            fun run() = println("左手访问身体信息:$bodyInfo")
        }

        inner class RightHand { // 右手
            fun run() = println("右手访问身体信息:$bodyInfo")
        }
    }
}

//嵌套类
class Outer {

    val info: String  = "OK"

    fun show() {
        Nested().output()
    }

    class Nested {

        fun output() = println("嵌套类")

    }
}

fun main() {
    // 内部类:
    Body("isOK").Heart().run()
    Body("isOK").Hand().LeftHand().run()
    Body("isOK").Hand().RightHand().run()

    // 嵌套类:
    Outer.Nested().output()

}

75、Kotlin的数据类

  • 普通类  set get 构造函数
  • 数据类--一般是用于 JavaBean的形式下,用数据类     set get 构造函数 解构操作 copy toString hashCode equals  数据类 生成 更丰富
// 普通类
class ResponseResultBean1(var msg: String, var code: Int, var data: String) : Any()
// set get 构造函数

// 数据类 -- 一般是用于 JavaBean的形式下,用数据类
data class ResponseResultBean2(var msg: String, var code: Int, var data: String) : Any()
// set get 构造函数 解构操作 copy toString hashCode equals  数据类 生成 更丰富


// 1.普通类 与 数据类 的 toString 背后原理
// 2.前面学习过的 == 与 ===
// 3.普通类的 == 背后原理
// 4.数据类的 == 背后原理
fun main() {
    // val (v1, v2, v3) =list  这个是list集合之前的 解构操作

    println(ResponseResultBean1("loginSuccess", 200, "登录成功的数据..."))
    // 普通类:: Any() toString Windows实现打印了   com.derry.s5.ResponseResultBean1@266474c2

    println(ResponseResultBean2("loginSuccess", 200, "登录成功的数据..."))
    // 数据类:: Any() 默认重写了 父类的 toString  打印子类的toString详情  ResponseResultBean2(msg=loginSuccess, code=200, data=登录成功的数据...)

    println()
    // =====================

    // 前面我们学习  == 值的比较 相当于java的equals      ===引用 对象 比较

    println( // 推理 两个 普通类 的值 是一样的,应该是true ,实际背后并不是这样的
        ResponseResultBean1("loginSuccess", 200, "登录成功的数据...") ==
                ResponseResultBean1("loginSuccess", 200, "登录成功的数据...")
    )
    // Any父类的 equals 实现 (ResponseResultBean1对象引用 比较 ResponseResultBean1对象引用)


    println(
        ResponseResultBean2("loginSuccess", 200, "登录成功的数据...") ==
                ResponseResultBean2("loginSuccess", 200, "登录成功的数据...")
    )
    // Any父类的 equals 被 数据类 重写了 equals 会调用 子类的 equals函数(对值的比较)
}

76、Kotlin的copy函数 

注意事项:使用copy的时候,由于内部代码只处理主构造,所以必须考虑次构造的内容

data class KtBase76 (var name: String, var age: Int) // 主构造
{
    var coreInfo : String = ""

    init {
        println("主构造被调用了")
    }

    // 次构造
    constructor(name: String) : this(name, 99) {
        println("次构造被调用")
        coreInfo = "增加非常核心的内容信息"
    }

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

/* 生成的toString 为什么只有两个参数?
   答:默认生成的toString 或者 hashCode equals 等等... 只管主构造,不管次构造
    public String toString() {
      return "KtBase76(name=" + this.name + ", age=" + this.age + ")";
    }
 */


fun main() {
    val p1 = KtBase76("李元霸") // 调用次构造初始化对象
    println(p1)

    val newP2 = p1.copy("李连杰", 78)
    println(newP2)

    // copy toString hashCode equals 等等... 主管主构造,不管次构造
}

77、Kotlin的解构声明

// 普通类
class Student1(var name: String , var age: Int, var sex: Char) {

    // 注意事项:顺序必须是 component1 component2 component3 和成员一一对应,顺序下来的
    operator fun component1() = name
    operator fun component2() = age
    operator fun component3() = sex
}

// 数据类
data class Student2Data(var name: String , var age: Int, var sex: Char)

fun main() {
    val(name, age, sex) = Student1("李四", 89, '男')
    println("普通类 结构后:name:$name, age:$age, sex:$sex")

    val(name1, age1, sex1) = Student2Data("李四", 89, '男')
    println("数据类 结构后:name:$name1, age:$age1, sex:$sex1")

    val(_, age2, _) = Student1("李四", 89, '男')
    println("数据类 结构后: age2:$age2")
}

78、Kotlin的运算符重载

  • 重载的修饰符是operator  
  • 请注意,一定要用 operator 关键字修饰,否则我们是不能用 + 号的
// 写一个数据类,就是为了,toString 打印方便而已哦
data class AddClass2(var number1: Int, var number2: Int) {
    operator fun plus(p1: AddClass2) : Int {
        return (number1 + p1.number1) + (number2 + p1.number2)
    }

    // 查看 整个KT可以用的  运算符重载 方式
    // operator fun AddClass2.
}

fun main() {
    // C++语言  +运算符重载就行了  -运算符重载就行了
    // KT语言  plus代表+运算符重载

    println(AddClass2(1, 1) + AddClass2(2, 2))
}

表示

重载

a = a + b

a = a.plus(b)

a = a - b

a = a.minus(b)

a = a * b

 a = a.tiems(b)

a = a / b 

a = a.div(b)

a = a % b 

 a = a.rem(b)

a … b 

 a.rangTo(b)

a % b 

a.rem(b) 或 a.mod(b)

++a, a++

inc

!a

not

a > b

a.compareTo(b) > 0

a < b

 a.compareTo(b) < 0

a >= b

a.compareTo(b) >= 0

a <= b

 a.compareTo(b) <= 0

a in b

b.contains(a)

a !in b  

!b.contains(a) 

Kotlin中的 && 、 || 、 ?: 、 === 、 !== 是不能被重载的

79、Kotlin的枚举类

// KT想表达:枚举其实也是一个class,为什么,就是为了 枚举可以有更丰富的功能
enum class Week {
    星期一,
    星期二,
    星期三,
    星期四,
    星期五,
    星期六,
    星期日
}

fun main() {
    println(Week.星期一)
    println(Week.星期四)

    // 枚举的值 等价于 枚举本身
    println(Week.星期二 is Week)   //true
}

80、Kotlin的枚举类定义函数

// 四肢信息class,我就是为了方便toString打印
data class LimbsInfo (var limbsInfo: String, var length: Int) {
    fun show() {
        println("${limbsInfo}的长度是:$length")
    }
}

enum class Limbs(private var limbsInfo: LimbsInfo) {
    LEFT_HAND(LimbsInfo("左手", 88)), // 左手
    RIGHT_HAND(LimbsInfo("右手", 88)), // 右手

    LEFT_FOOT(LimbsInfo("左脚", 140)), // 左脚
    RIGHT_FOOT(LimbsInfo("右脚", 140)) // 右脚

    ; // 结束枚举值

    // 1. WEEK 这个时候 再定义单调的 枚举值,就报错了,必须所有枚举值,保持一致的效果
    // 2. 枚举的 主构造的参数 必须和 枚举(的参数) 保持一致

    fun show() = "四肢是:${limbsInfo.limbsInfo}的长度是:${limbsInfo.length}"

    fun updateData(limbsInfo: LimbsInfo) {
        println("更新前的数据是:${this.limbsInfo}")
        this.limbsInfo.limbsInfo = limbsInfo.limbsInfo
        this.limbsInfo.length = limbsInfo.length
        println("更新后的数据是:${this.limbsInfo}")
    }
}

fun main() {
    // 显示枚举值

    // 一般不会这样用
    /*println(Limbs.show())
    println(Limbs().show())*/

    // 一般的用法如下:
    println(Limbs.LEFT_HAND.show())
    println(Limbs.RIGHT_HAND.show())
    println(Limbs.LEFT_FOOT.show())
    println(Limbs.RIGHT_FOOT.show())

    println()

    // 更新枚举值
    Limbs.RIGHT_HAND.updateData(LimbsInfo("右手2", 99))
    Limbs.LEFT_HAND.updateData(LimbsInfo("左手2", 99))
    Limbs.LEFT_FOOT.updateData(LimbsInfo("左脚2", 199))
    Limbs.RIGHT_FOOT.updateData(LimbsInfo("右叫2", 199))
}

81、Kotlin的代数数据类型

enum class Exam {
    Fraction1, // 分数差
    Fraction2, // 分数及格
    Fraction3, // 分数良好
    Fraction4, // 分数优秀

    ; // 枚举结束

    // 需求 得到优秀的孩子姓名
    var studentName: String? = null
    // 我们用枚举类,要做到此需求,就非常的麻烦了,很难做到而已,不是做不到
    //  需求:引出 密封类
}

class Teacher (private val exam: Exam) {
    fun show() =
        when (exam) {
            Exam.Fraction1 -> "该学生分数很差"
            Exam.Fraction2 -> "该学生分数及格"
            Exam.Fraction3 -> "该学生分数良好"
            Exam.Fraction4 -> "该学生分数优秀"
            // else -> 由于我们的show函数,是使用枚举类类型来做判断处理的,这个就属于 代数数据类型,就不需要写 else 了
            // 因为when表达式非常明确了,就只有 四种类型,不会出现 else 其他,所以不需要写
        }
}

// 1.定义枚举Exam类,四个级别分数情况
// 2.定义Teacher老师类,when使用枚举类
// 3.需求 得到优秀的孩子姓名
fun main() {
    println(Teacher(Exam.Fraction1).show())
    println(Teacher(Exam.Fraction3).show())
}

82、Kotlin的密封类  sealed

// 密封类,我们成员, 就必须有类型 并且 继承本类
sealed class Exams {
    // object? Fraction1 Fraction3 都不需要任何成员,所以一般都写成object,单例就单例,无所谓了
    object Fraction1 : Exams() // 分数差
    object Fraction2 : Exams() // 分数及格
    object Fraction3 : Exams() // 分数良好

    // 假设 Fraction4 是可以写object的,那么也不合理,因为对象不是单例的,有 对象1李四 对象2王五
    class Fraction4(val studentName : String) : Exams() // 分数优秀

    // 需求 得到优秀的孩子姓名
    // var studentName: String? = null
    // 我们用枚举类,要做到此需求,就非常的麻烦了,很难做到而已,不是做不到
    //  需求:引出 密封类
}

class Teachers (private val exam: Exams) {
    fun show() =
        when (exam) {
            is Exams.Fraction1 -> "该学生分数很差"
            is Exams.Fraction2 -> "该学生分数及格"
            is Exams.Fraction3 -> "该学生分数良好"
            is Exams.Fraction4 -> "该学生分数优秀:该学生的姓名是:${(this.exam as Exams.Fraction4).studentName}"
        }
}


fun main() {
    println(Teachers(Exams.Fraction1).show())
    println(Teachers(Exams.Fraction2).show())
    println(Teachers(Exams.Fraction3).show())
    println(Teachers(Exams.Fraction4("李四")).show()) // 对象1
    println(Teachers(Exams.Fraction4("王五")).show()) // 对象2

    println(Exams.Fraction1 === Exams.Fraction1) // true, === 必须对象引用, object是单例 只会实例化一次

    println(Exams.Fraction4("AAA") === Exams.Fraction4("AAA")) // class 有两个不同的对象,所以是false
}

83、数据类使用条件

  1. 条件一:服务器请求回来的响应的 JavaBean  LoginResponseBean 基本上可以使用 数据类
  2. 条件二:数据类至少必须有一个参数的主构造函数
  3. 条件三:数据类必须有参数, var val 的参数
  4. 条件四:数据类不能使用 abstract,open,sealed,inner 等等 修饰 (数据类,数据载入的事情 数据存储)
  5. 条件五:需求 比较,copy,toString,解构,等等 这些丰富的功能时,也可以使用数据类

84、Kotlin的接口定义 

 1.接口里面的所有成员 和 接口本身 都是 public open 的,所以不需要open,这个是接口的特殊
 2.接口不能有主构造,反正就是没有构造
 3.实现类不仅仅要重写接口的函数,也要重写 接口的成员
 4.接口实现代码区域,全部都要增加 override 关键字来修饰

interface IUSB {
    var usbVersionInfo: String // USB版本相关的信息
    var usbInsertDevice: String // USB插入的设备信息

    fun insertUBS() : String
}

// 鼠标UBS实现类
class Mouse(override var usbVersionInfo: String = "USB 3.0",
            override var usbInsertDevice: String = "鼠标接入了UBS口") :IUSB {

    override fun insertUBS() = "Mouse $usbVersionInfo, $usbInsertDevice"
}

// 键盘USB实现类
class KeyBoard : IUSB {

    override var usbVersionInfo: String = "USB 3.1"
        // 下面的 set get 都会持有 field,现在是你没有给 usbVersionInfo 赋值, 意味着field是没法让set/get持有的
        get() = field
        set(value) {
            field = value
        }

    override var usbInsertDevice: String = "键盘接入了UBS口"
        get() {
            println("@你get了[${field}]值出去了")
            return field
        }
        set(value) {
            field = value
            println("@你set了[${value}]值进来了")
        }

    override fun insertUBS(): String = "KeyBoard $usbVersionInfo, $usbInsertDevice"
}

fun main() {
    val iusb1 : IUSB = Mouse()
    println(iusb1.insertUBS())

    println()

    val iusb2: IUSB = KeyBoard()
    println(iusb2.insertUBS())

    iusb2.usbInsertDevice = "AAA"
}

85、Kotlin语言的接口的默认实现

interface USB2 {

    // 1.接口 var 也是不能给接口的成员赋值的 (但是有其他办法)
    // 2.任何类 接口 等等  val 代表只读的,是不可以在后面动态赋值 (也有其他办法)

    val usbVersionInfo: String // USB版本相关的信息
       get() = (1..100).shuffled().last().toString()
       // val 不需要set

    val usbInsertDevice: String // USB插入的设备信息
        get() = "高级设备接入USB"
        // val 不需要set

    fun insertUBS() : String
}

// 鼠标UBS实现类
class Mouse2 : USB2 {

    override val usbInsertDevice: String
        get() = super.usbInsertDevice

    override val usbVersionInfo: String
        get() = super.usbVersionInfo

    override fun insertUBS() = "Mouse $usbVersionInfo, $usbInsertDevice"
}

// 1.注意:这样做是不对的,因为接口成员本来就是用来声明标准的
//        但是可以在接口成员声明时,完成对接口成员的实现
fun main() {
    val iusb1 = Mouse2()
    println(iusb1.insertUBS())
}

86、Kotlin的抽象类(abstract)

abstract class BaseActivity {

    fun onCreate() {
        setContentView(getLayoutID())

        initView()
        initData()
        initXXX()
    }

    private fun setContentView(layoutID: Int) = println("加载{$layoutID}布局xml中")

    abstract fun getLayoutID(): Int
    abstract fun initView()
    abstract fun initData()
    abstract fun initXXX()
}

class MainActivity : BaseActivity() {

    override fun getLayoutID(): Int = 564

    override fun initView() = println("做具体初始化View的实现")

    override fun initData() = println("做具体初始化数据的实现")

    override fun initXXX() = println("做具体初始化XXX的实现")

    fun show() {
        super.onCreate()
    }
}

class LoginActivity : BaseActivity() {

    override fun getLayoutID(): Int = 564

    override fun initView() = println("做具体初始化View的实现")

    override fun initData() = println("做具体初始化数据的实现")

    override fun initXXX() = println("做具体初始化XXX的实现")

    fun show() {
        super.onCreate()
    }
}

fun main() = LoginActivity().show()

87、Kotlin的定义泛型类

class KtBase87<T> (private val obj: T) { // 万能输出器
    fun show() = println("万能输出器:$obj")
}

data class Student(val name: String , val age: Int, val sex: Char)
data class Teacher(val name: String , val age: Int, val sex: Char)

// 1.定义 对象输出器 println(obj)
// 2.定义两个对象,三个属性
// 3.对象 String Int Double Float Char 等 测试 对象输出器
fun main() {
    val stu1 = Student("张三", 88, '男')
    val stu2 = Student("李四", 78, '女')

    val tea1 = Teacher("王五", 77, '男')
    val tea2 = Teacher("赵六", 89, '女')

    KtBase87(stu1).show()
    KtBase87(stu2).show()
    KtBase87(tea1).show()
    KtBase87(tea2).show()

    KtBase87(String("刘一".toByteArray())).show()

    KtBase87(575).show()
    KtBase87(53456.45).show()
    KtBase87(4645.5f).show()
    KtBase87('男').show()

}

88、Kotlin的泛型函数

// 1.万能对象返回器 Boolean来控制是否返回 运用 takeIf
class KtBase104<T>(private val isR: Boolean, private val obj: T) {

    fun getObj() : T? = obj.takeIf { isR }

}

// 1.万能对象返回器 Boolean来控制是否返回 运用 takeIf
// 2.四个对象打印
// 3.对象打印 + run + ?:
// 4.对象打印 + apply + ?:
// 5.show(t: T) + apply + ?:
fun main() {
    val stu1 = Student("张三", 88, '男')
    val stu2 = Student("李四", 78, '女')

    val tea1 = Teacher("王五", 77, '男')
    val tea2 = Teacher("赵六", 89, '女')

    // 2.四个对象打印
    println(KtBase104(true, stu1).getObj())
    println(KtBase104(true, stu2).getObj())
    println(KtBase104(true, tea1).getObj())
    println(KtBase104(true, tea2).getObj())

    println(KtBase104(false, tea2).getObj() ?: "大哥,你万能对象返回器,是返回null啊")

    println()

    // 3.对象打印 + run + ?:
    val r : Any = KtBase104(true, stu1).getObj() ?.run {
        // 如果 getObj 返回有值,就会进来
        // this == getObj本身
        println("万能对象是:$this") // 返回Unit
        545.4f // 返回Float
    } ?: println("大哥,你万能对象返回器,是返回null啊") // 返回Unit
    println(r)

    println()

    // apply特点:永远都是返回 getObj.apply  getObj本身
    val r2 : Student = KtBase104(true, stu2).getObj().apply {  }!!
    println("r2:$r2")

    // 4.对象打印 + apply + ?:
    val r3: Teacher = KtBase104(true, tea1).getObj() .apply {
        // this == getObj本身

        if (this == null) {
            println("大哥,你万能对象返回器,是返回null啊")
        } else {
            println("万能对象是:$this")
        }
    }!!
    println("r3:$r3")

    println()

    show("Derry")
    show("Kevin")
    show("OK")
    show(null)

    println()

    show2("111")
    show2("222")
    show2("OK")
    show2(null)
}

// 5.show(t: T) + also + ?:
fun <B> show(item: B) {
    item ?.also {
        // it == item本身
        println("万能对象是:$it")
    } ?: println("大哥,你万能对象返回器,是返回null啊")
}

fun <B> show2(item: B) {
    // var r0 = item

    var r : B? = item ?.also {
       if (it == null) {
           println("大哥,你万能对象返回器,是返回null啊")
       } else {
           println("万能对象是:$it")
       }
    } ?: null
    println("show2: 你传递进来的r:$r")
}

89、Kotlin的泛型变换实战

// 1.类 isMap map takeIf  map是什么类型
class KtBase89<T>(val isMap: Boolean = false, val inputType: T) {

    // 模仿RxJava  T是要变化的输入类型   R是变换后的输出类型
    // 要去map返回的类型是 R?  == 有可能是R 有可能是null
    inline fun <R> map(mapAction: (T) -> R) = mapAction(inputType).takeIf { isMap }
}

inline fun <I, O> map(inputValue : I , isMap: Boolean = true, mapActionLambda : (I) -> O) =
    if (isMap) mapActionLambda(inputValue) else null

// 1.类 isMap map takeIf  map是什么类型
// 2.map int -> str 最终接收是什么类型
// 3.map per -> stu 最终接收是什么类型
// 4.验证是否是此类型 与 null
fun main() {

    // 2.map int -> str 最终接收是什么类型
    val p1 = KtBase89(isMap = /*true*/ false, inputType = 5434)

    val r = p1.map {
        it
        it.toString() // lambda最后一行是 返回值
        "我的it是:$it" // lambda最后一行是 返回值
    }

    // 4.验证是否是此类型 与 null
    val str1: String = "OK1"
    val str2: String? = "OK2"
    println(r is String)
    println(r is String?)
    println(r ?: "大哥你是null,你在搞什么飞机...,你是不是传入了isMap是false")

    println()

    // 3.map per -> stu 最终接收是什么类型
    val p2 = KtBase105(true, Persons("李四", 99))
    val r2 : Students? = p2.map {
        // it == Persons对象 == inputType
        it
        Students(it.name, it.age)
    }
    println(r2)

    println()

    // map函数 模仿RxJava变换操作
    val r3 = map(123) {
        it.toString()
        "map包裹[$it]" // lambda表达式最后一行,就是返回值
    }
    println(r3)

    123.run {  }
}

data class Persons(val name: String, val age: Int)
data class Students(val name: String, val age: Int)

90、Kotlin语言的泛型类型约束

open class MyAnyClass(name: String) // 祖宗类 顶级父类

open class PersonClass(name: String) : MyAnyClass(name = name) // 父类

class StudentClass(name: String) : PersonClass(name = name) // 子类

class TeacherClass(name: String) : PersonClass(name = name) // 子类

class DogClass(name: String) // 其他类 另类

class CatClass(name: String) // 其他类 另类

// TODO 106-Kotlin语言的泛型类型约束学习
// T : PersonClass   相当于  Java的 T extends PersonClass
// PersonClass本身 与 PersonClass的所有子类 都可以使用, 其他的类,都不能兼容此泛型
class KtBase106<T : PersonClass> (private val inputTypeValue: T, private val isR: Boolean = true) {

    // 万能对象返回器
    fun getObj() = inputTypeValue.takeIf { isR }
}

fun main() {
    val any = MyAnyClass("111")// 祖宗类 顶级父类

    val per = PersonClass("222") // 父类

    val stu = StudentClass("333") // 子类
    val pea = TeacherClass("444") // 子类

    val dog = DogClass("555") // 其他类 另类

    /*val r1 = KtBase106(any).getObj() // 报错了,类型限定了
    println(r1)*/

    val r2 = KtBase106(per).getObj()
    println(r2)

    val r3 = KtBase106(stu).getObj()
    println(r3)

    val r4 = KtBase106(stu).getObj()
    println(r4)

    /*val r5 = KtBase106(dog).getObj() // 报错了,类型限定了
    println(r5)*/

    // KtBase106(CatClass("cat 小白")) // 报错了,类型限定了
}