Kotlin学习笔记

1.空检查

?:表示为空则不执行后面
!!:表示一定执行后面

    // TODO 第一种情况:默认是不可空类型,所以不能给null
    var name: String = "Derry"

    // 提示:不能是非空类型String的值
    // name = null

    println(name)

    // TODO 第二种情况:声明时指定为可空类型
    var name2: String ?
    name2 = null
    // name2 = "Derry"
    println(name2)

思考题:kotlin是静态语言还是动态语言?
静态语言,因为在编译期就决定了类型。

2.数据类型

String      字符串
Char        单字符
Boolean     true/false
nt         整形
Double      小数
List        集合
set         无重复的元素集合
map         键值对的集合

int   ---> java int
float  ---> java float

// Java语言有两种数据类型:
//      第一种:基本类型:int double 等
//      第二种:引用类型 String 等
// Kotlin语言只有一种数据类型:
//      看起来都是引用类型,实际上编译器会在Java字节码中,修改成 “基本类型”,所以不会耗费性能

3.in表达式

if (number in 10..59) {//含头含尾
   println("不及格")
} 

4.when表达式

    // Java的 if 语句
    // KT的 if 是表达式 有返回值的

    val info = when(week) {
        1 -> "今天是星期一,非常忙碌的一天开会"
        2 -> "今天是星期二,非常辛苦的写需求"
        3 -> "今天是星期三,努力写Bug中"
        4 -> "今天是星期四,发布版本到凌晨"
        5 -> "今天是星期五,淡定喝茶,一个Bug改一天"
        6 -> "今天是星期六,稍微加加班"
        7 -> "今天是星期七,看剧中,游玩中"
        else -> {
            println("养猪去了,忽略星期几")
        }
    }
    println(info) //Kolint.Unit

5.字符串模板

 	val garden = "黄石公园"
    val time = 6

    println("今天天气很晴朗,去玩" + garden + ",玩了" +time +" 小时") // Java的写法
    println("今天天气很晴朗,去${garden}玩,玩了$time 小时") // 字符串模版的写法

    // KT的if是表达式,所以可以更灵活,  Java的if是语句,还有局限性
    val isLogin = false
    println("server response result: ${if (isLogin) "恭喜你,登录成功√" else "不恭喜,你登录失败了,请检查Request信息"}")

6.函数头

在这里插入图片描述

// 函数默认都是public
// 其实Kotlin的函数,更规范,先有输入,再有输出
private fun method01(age: Int, name: String) : Int {
    println("你的姓名是:$name,你的年龄是:$age")
    return 200
}

/* 上面的Kt函数,背后会变成下面的Java代码:
   private static final int method01(int age, String name) {
      String var2 = "你的姓名是:" + name + ",你的年龄是:" + age;
      boolean var3 = false;
      System.out.println(var2);
      return 200;
   }
 */

7.默认参数

// TODO 17.Kotlin中函数参数的默认参数
fun main() {
    action01("lisi", 89)
    action02("wangwu")
    action03()

    action03("赵六", 76)
}

private fun action01(name: String, age: Int) {
    println("我的姓名是:$name, 我的年龄是:$age")
}

private fun action02(name: String, age: Int = 77) {
    println("我的姓名是:$name, 我的年龄是:$age")
}

private fun action03(name: String = "王五", age: Int = 67) {
    println("我的姓名是:$name, 我的年龄是:$age")
}

8.具名参数

// TODO 18.Kotlin语言的具名函数参数
fun main() {
    loginAction(age = 99, userpwd = "123", usernam = "de", username = "Derry", phonenumber = "123456")
}

private fun loginAction(username: String, userpwd: String, phonenumber: String, age: Int, usernam: String) {
    println("username:$username, userpwd:$userpwd, phonenumber:$phonenumber, age:$age")
}

9.Unit类型

// Java语言的void关键字(void是 无参数返回的 忽略类型) 但是他是关键帧啊,不是类型,这很矛盾
// : Unit不写,默认也有,Unit代表  无参数返回的 忽略类型 == Unit类型类
private fun doWork() : Unit {
    return println()
}

private fun doWork2() {
    return println()
}

10.Nothing类型特点

// TODO 20.Kotlin语言的Nothing类型特点
fun main() {
    show(-1)
}

private fun show(number: Int) {
    when(number) {
        -1 -> TODO("没有这种分数")
        in 0..59 -> println("分数不及格")
        in 60..70 -> println("分数及格")
        in 71..100 -> println("分数优秀")
    }
}

interface A {
    fun show()
}

class AImpl : A {
    override fun show() {
        // 下面这句话,不是注释提示,会终止程序的
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

}

11.反引号函数名

反引号可以用来修饰函数名

// TODO 21.Kotlin语言的反引号中函数名特点
fun main() {
    // 第一种情况:
    `登录功能 2021年8月8日测试环境下 测试登录功能 需求编码人是Derry`("Derry", "123456")

    // 第二种情况:// in  is  在kt里面就是关键字,怎么办呢? 使用反引号
    KtBase21.`is`()
    KtBase21.`in`()

    // 第三种情况: 很少发生
    `65465655475`()
}

private fun `登录功能 2021年8月8日测试环境下 测试登录功能 需求编码人是Derry`(name: String, pwd: String) {
    println("模拟:用户名是$name, 密码是:$pwd")
}

private fun `65465655475`() {
    // 写了很复杂的功能,核心功能
    // ...
}

// 公司加密私有的文档     65465655475  === 此函数的作用 xxxx
// 公司加密私有的文档     55576575757  === 此函数的作用 xxxx

12.函数

1.匿名函数

fun main() {
    val len = "Derry".count()
    println(len)//5

    val len2 = "Derry".count {
        // it 等价于 D e r r y 的字符 Char
        it == 'r'
    }
    println(len2)//2
}

2.函数类型&隐式返回

fun main() {
    // 第一步:函数输入输出的声明
    val methodAction : () -> String

    // 第二步:对上面函数的实现
    methodAction = {
        val inputValue = 999999
        "$inputValue Derry" // == 背后隐式 return "$inputValue Derry";
        // 匿名函数不要写return,最后一行就是返回值
    }

    // 第三步:调用此函数
    println(methodAction())
}

/*
fun methodAction() : String {
    return "Derry"
}
 */

2.函数参数

fun main() {
    // 第一步:函数输入输出的声明   第二步:对声明函数的实现
    val methodAction : (Int, Int, Int) -> String = { number1, number2, number3 ->
        val inputValue = 999999
        "$inputValue Derry 参数一:$number1, 参数二:$number2, 参数三:$number3"
    }
    // 第三步:调用此函数
    println(methodAction(1, 2, 3))
}

/*
fun methodAction(number1: Int, number2: Int, number3: Int) : String {
    val inputValue = 999999
    return "$inputValue Derry 参数一:$number1, 参数二:$number2, 参数三:$number3"
}
 */

3.it关键字

fun main() {
    val methodAction : (Int, Int, Int) -> String = { n1, n2, n3 ->
        val number = 24364
        println("$number Derry ,n1:$n1, n2:$n2, n3:$n3")
        "$number Derry ,n1:$n1, n2:$n2, n3:$n3"
    }
    // methodAction.invoke(1,2,3)
    methodAction(1,2,3)

    val methodAction2 : (String) -> String = { "$it Derry" }
    println(methodAction2("DDD"))//DDD Derry

    val methodAction3 : (Double) -> String = { "$it Derry2" }
    println(methodAction3(5454.5))// 5454.5 Derry2
}

/*
    fun methodAction2(it : String) : String { return "$it Derry" }
 */

4.匿名函数的类型推断

fun main() {
    // 匿名函数,类型推断为String
    // 方法名 : 必须指定 参数类型 和 返回类型
    // 方法名 = 类型推断返回类型
    val method1 = { v1:Double, v2:Float, v3:Int ->
       "v1:$v1, v2:$v2, v3:$v3"
    } // method1 函数: (Double, Float, Int) -> String
    println(method1(454.5, 354.3f, 99))

    val method2 = {
        3453.3f
    } // method2 函数: () -> Unit
    println(method2())

    val method3 = { number: Int ->
        number
    } // method3 函数: (Int) -> Int
    println(method3(9))
}

5.lambda学习

fun main() {

    // 匿名函数 == lambda表达式
    val addResultMethod = { number1 : Int, number2: Int ->
        "两数相加的结果是:${number1 + number2}"
    } // addResultMethod 函数: (Int, Int) -> String
    println(addResultMethod(1, 1))

    // 匿名函数 入参 Int,          返回 Any类型
    // lambda表达式的参数 Int,    lambda表达式的结果Any类型
    val weekResultMethod = { number: Int ->
        when(number) {
            1 -> "星期1"
            2 -> "星期2"
            3 -> "星期3"
            4 -> "星期4"
            5 -> "星期5"
            else -> -1
        }
    } // weekResultMethod 函数: (Int) -> Any
    println(weekResultMethod(2))

    // 匿名函数 属于 lambda
}

6.入参为函数

普通写法

fun main() {
	// 第一种方式  匿名函数
    loginAPI("Derry", "123456") { msg: String, code: Int ->
        println("最终登录的情况如下: msg:$msg, code:$code")
    }

	 // 第二种方式
    loginAPI2("Derry", "123456", { msg: String, code:Int ->
        println("最终登录的情况如下: msg:$msg, code:$code")
    })

    // 第三种方式
    loginAPI2("Derry", "123456", responseResult = { msg: String, code: Int ->
        println("最终登录的情况如下: msg:$msg, code:$code")
    })

	 // 第四种方式
	 // 具名函数
    //函数引用 lambda属于函数类型的对象,需要把methodResponseResult普通函数变成 函数类型的对象(函数引用)
	login("Derry2", "123456", ::methodResponseResult)
}

fun methodResponseResult(msg: String, code: Int) {
    println("最终登录的成果是:msg:$msg, code:$code")
}

const val USER_NAME_SAVE_DB = "Derry"
const val USER_PWD_SAVE_DB = "123456"

// 如果函数参数有lambda,尽量使用 inline关键帧,这样可以减少 函数开辟、对象开辟的损耗
inline fun loginAPI(username: String, userpwd: String, responseResult: (String, Int) -> Unit) {
    if (username == null || userpwd == null) {
        TODO("用户名或密码为null") // 出现问题,终止程序
    }
    if (username.length > 3 && userpwd.length > 3) {
        if (wbeServiceLoginAPI(username, userpwd)) {
            responseResult("login success", 200)
        } else {
            responseResult("login error", 444)
        }
    } else {
        TODO("用户名和密码不合格") // 出现问题,终止程序
    }
}

// 登录的API暴露者 服务器
private fun wbeServiceLoginAPI(name: String, pwd: String) : Boolean {
    // kt的if是表达式(很灵活)     java的if是语句(有局限性)
    return if (name == USER_NAME_SAVE_DB && pwd == USER_PWD_SAVE_DB) true else false
}

Java写法


interface ResponseResult {
    void result(String msg, int code);
}

public class KTBase29 {
    public final static String USER_NAME_SAVE_DB = "Derry";
    public final static String USER_PWD_SAVE_DB = "123456";

    public static void main(String[] args) {
        loginAPI("Derry2", "123456", new ResponseResult() {
            @Override
            public void result(String msg, int code) {
                System.out.println(String.format("最终登录的情况如下: msg:%s, code:%d", msg, code));
            }
        });
    }

    // 登录API 模仿 前端
    public static void loginAPI(String username, String userpwd, ResponseResult responseResult) {
        if (username == null || userpwd == null) {
            // TODO("用户名或密码为null") // 出现问题,终止程序
        }
        if (username.length() > 3 && userpwd.length() > 3) {
            if (wbeServiceLoginAPI(username, userpwd)) {
                responseResult.result("login success", 200);
            } else {
                responseResult.result("login error", 444);
            }
        } else {
            // TODO("用户名和密码不合格") // 出现问题,终止程序
        }
    }

    // 登录的API暴露者 服务器
    private static boolean wbeServiceLoginAPI(String name, String pwd) {
        // kt的if是表达式(很灵活)     java的if是语句(有局限性)
        if (name == USER_NAME_SAVE_DB && pwd == USER_PWD_SAVE_DB)
            return true;
        else
            return false;
    }
}

6.返回类型为函数

fun main() {
    val r = show("学习KT语言")
    // r 是show函数的 返回值

    val niming_showMethod = showMethod("show")
    // niming_showMethod 是 showMethod函数的返回值 只不过这个返回值 是一个 函数

    // niming_showMethod == 匿名函数
    println(niming_showMethod("Derry", 33))
}

fun show(info: String): Boolean {
    println("我是show函数 info:$info")
    return true
}

fun show2(info: String): String {
    println("我是show函数 info:$info")
    return "DDD"
}

// showMethod函数 再返回一个 匿名函数
fun showMethod(info: String): (String, Int) -> String {
    println("我是show函数 info:$info")

    // return 一个函数 匿名函数
    return { name: String, age: Int ->
        "我就是匿名函数:我的name:$name, age:$age"
    }
}

7.匿名函数跟具名函数

fun main() {
    // 匿名函数
    showPersonInfo("lisi", 99, '男', "学习KT语言") {
        println("显示结果:$it")
    }

    // 具名函数 showResultImpl
    showPersonInfo("wangwu", 89, '女', "学习C++语言", ::showResultImpl)
}

fun showResultImpl(result: String) {
    println("显示结果:$result")
}

inline fun showPersonInfo(name: String, age: Int, sex: Char, study: String, showResult: (String) -> Unit) {
    val str = "name:$name, age:$age, sex:$sex, study:$study"
    showResult(str)
}

Java写法

interface IShowResult { // 接口的折中方案 解决 kt的lambda问题
    void result(String result);
}

// TODO 34.Kotlin语言的匿名函数与具名函数
public class KtBase34 {

    public static void main(String[] args) { // psv
        // 匿名函数 - 匿名接口实现
        showPersonInfo("lisi", 99, 'm', "study cpp", new IShowResult() {
            @Override
            public void result(String result) {
                System.out.println("显示结果:" + result);
            }
        });

        // 具名函数 - 具名接口实现 showResultImpl
        IShowResult showResultImpl = new MshowResultImpl();
        showPersonInfo("wangwu", 88, 'n', "study kt", showResultImpl);
    }

   static class MshowResultImpl implements IShowResult {

        @Override
        public void result(String result) {
            System.out.println("显示结果:" + result);
        }
    }

    static void showPersonInfo(String name, int age, char sex, String study, IShowResult iShowResult) {
        String str = String.format("name:%s, age:%d, sex:%c, study:%s", name, age, sex, study);
        iShowResult.result(str);
    }
}

13.操作符

1.let

fun main() {
    var name: String? = null
    name = "Derry"
    name = ""

    // name是可空类型的 如果真的是null,?后面这一段代码不执行,就不会引发空指针异常
    val r =name?.let {
        // it == name 本身
        // 如果能够执行到这里面的,it 一定不为null

        if (it.isBlank()) { // 如果name是空值 "" 没有内容
            "Default"
        } else {
            "[$it]"
        }
    }
    println(r)
}
// 普通方式 对集合第一个元素相加
// let方式 对集合第一个元素相加
// 普通方式 对值判null,并返回
// let方式 对值判null,并返回
fun main() {
    // 普通方式 对集合第一个元素相加
    val list = listOf(6, 5, 2, 3, 5, 7)
    val value1 = list.first() // 第一个元素
    val result1 = value1 + value1
    println(result1) //12

    // let方式 对集合第一个元素相加
    val result2 = listOf(6, 5, 2, 3, 5, 7).let {
        // it == list集合
        it.first() + it.first() // 匿名函数的最后一行,作为返回值,let的特点,   但是前面学的apply永远是返回info本身
        /*true
        true
        true*/
    }
    println(result2) //12

    println()

    // 普通方式 对值判null,并返回
    println(getMethod1(/*null*/ "Derry")) 

    // let方式 + 空合并操作符 对值判null,并返回
    println(getMethod3(/*null*/ "Derry"))
}

// 普通方式 对值判null,并返回
fun getMethod1(value: String?) : String {
    return if (value == null) "你传递的内容是null" else "欢迎回来${value}非常欢迎"
}
// 普通方式 简化版本
fun getMethod2(value: String?) = if (value == null) "你传递的内容是null" else "欢迎回来${value}非常欢迎"

// let方式 + 空合并操作符 对值判null,并返回
fun getMethod3(value: String?) : String {
    return value?.let {
        "欢迎回来${it}非常欢迎"
    } ?: "你传递的内容是null"
}

// let方式 + 空合并操作符 对值判null,并返回 简化版本
fun getMethod4(value: String?) =
     value?.let {
        "欢迎回来${it}非常欢迎"
    } ?: "你传递的内容是null"

2. ?: 空合并操作符

fun main() {
    var info: String? = "李小龙"
    // info = null

    // 空合并操作符  xxx ?: "原来你是null啊" 
    // "如果xxx等于null,就会执行 ?: 后面的区域"
    println( info ?: "原来你是null啊" )

    // let函数 + 空合并操作符
    println(info?.let { "【$it】" } ?: "原来你是null啊2")
}

3.自定义异常

fun main() {
   try {
       var info: String? = null
       checkException(info)
       println(info!!.length)
   }catch (e: Exception) {
       println("啊呀:$e")
   }
}

fun checkException(info: String?) {
   info ?: throw CustomException()
}

class CustomException : IllegalArgumentException("你的代码太不严谨了")

4.先决条件函数

fun main() {
    var value1: String ? = null
    var value2: Boolean = false

	// 经常使用来检测是否为null
    // checkNotNull(value1) // java.lang.IllegalStateException: Required value was null.
    // requireNotNull(value1) // java.lang.IllegalArgumentException: Required value was null.

	// 经常使用来检测是否为true
    require(value2) // java.lang.IllegalArgumentException: Failed requirement.
}

5.substring

const val INFO = "Derry is Success Result"

// TODO 43.Kotlin语言的substring
fun main() {
    val indexOf = INFO.indexOf('i')
    println(INFO.substring(0, indexOf)) //Derry 
    println(INFO.substring(0 until indexOf)) // Derry  KT基本上用此方式: 0 until indexOf
}

6.split

fun main() {
    val jsonText = "Derry, Leo, Lance, Alvin"
    // list 自动类型推断成 list == List<String>
    val list = jsonText.split(",")

    // 直接输出 list 集合,不解构
    println("$list") //[Derry, Leo, Lance, Alvin]

    // C++ 有解构  Kt也有解构
    val (v1, v2, v3, v4) = list
    println("v1:$v1, v2:$v2, v3:$v3, v4:$v4") // v1:Derry, v2:Leo, v3:Lance, v4:Alvin
}

7.replace

fun main() {
   val sourcePwd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    println("原始密码是:$sourcePwd")

    // 加密操作:就是把字符替换成数字 打乱了,就属于加密了
    val newPwd = sourcePwd.replace(Regex("[AKMNO]")) {
        it.value // 完全没有做任何事情

        when(it.value) { // 这里的每一个字符 A B C D ...
            "A" -> "9"
            "K" -> "3"
            "M" -> "5"
            "N" -> "1"
            "O" -> "4"
            else -> it.value // 就啥事不做,直接返回 字符本身 A B C D ...
        }
    }
    println("加密后的密码是:$newPwd") // 9BCDEFGHIJ3L514PQRSTUVWXYZ
}

8. ==跟 ===的区别

fun main() {
    // == 值 内容的比较  相当于Java的equals
    // === 引用的比较

    val name1 : String = "Derry"
    val name2 : String = "Derry"
    val name3 = "ww"

    // 小结:name1.equals(name2)  等价于 name1 == name2  都是属于 值 内容的比较
    println(name1.equals(name2)) // true,java
    println(name1 == name2) // true,kt

    // 引用的比较
    println(name1 === name2) // true
    println(name1 === name3) // false

    // 引用的比较 难度高一点点
    val name4 = "derry".capitalize() // 修改成"Derry"
    println(name4 === name1) // false
}

9.字符串遍历forEach

fun main() {
    val str = "ABCDE"
    str.forEach {  c -> // 覆盖默认的it参数名,修改参数名为 c
        // it == str的每一个字符 A B C D ...
        // print("$it  ")
        print("$c  ") //A  B  C  D  E
    }
}

10.toInt(),toIntOrNull()

// 小结:以后字符串有整形相关的转换,尽量用 toIntOrNull 此函数
fun main() {
    val number: Int = "666".toInt()
    println(number) //666

    // 字符串里面放入了Double类型,无法转换成Int,会奔溃
    // val number2: Int = "666.6".toInt()
    // println(number2)

    // 解决什么奔溃的问题
    val number2: Int? = "666.6".toIntOrNull()
    println(number2) //null

    val number3: Int? = "888".toIntOrNull()
    println(number3) //888

    val number4: Int? = "888.8".toIntOrNull()
    println(number4 ?: "原来你是null啊")
}

11.roundToInt()

fun main() {
    println(65.4645654.toInt()) // 65 
	println(65.8343433.roundToInt()) // 65

    println(65.4645654.roundToInt())  // 65 四舍五入
    println(65.8343433.roundToInt()) // 66 四舍五入

    // 结论:用 roundToInt()函数,保证 Double ->转Int 持有四舍五入的效果

    // r的类型: String
    val r  = "%.3f".format(65.8343433)
    println(r) //65.834
}

12.apply、

特点:持有this,本身作为返回值

fun main() {
    val info = "Derry You Hao"

    // 普通的方式
    println("info字符串的长度是:${info.length}") //13
    println("info最后一个字符是:${info[info.length -1]}") //o
    println("info全部转成小写是:${info.toLowerCase()}") //derry you hao

    println()

    // apply内置函数的方式
    // info.apply特点:apply函数始终是返回 info本身 String类型
    val infoNew : String = info.apply {
        // 一般大部分情况下,匿名函数,都会持有一个it,但是apply函数不会持有it,却会持有当前this == info本身
        println("apply匿名函数里面打印的:$this") //Derry You Hao

        println("info字符串的长度是:${length}") //13
        println("info最后一个字符是:${this[length -1]}") //o
        println("info全部转成小写是:${toLowerCase()}") //derry you hao
    }
    println("apply返回的值:$infoNew") //Derry You Hao

    println()

    // 真正使用apply函数的写法规则如下:
    // info.apply特点:apply函数始终是返回 “本身”,所以可以链式调用
    info.apply {
        println("长度是:$length") //13
    }.apply {
        println("最后一个字符是:${this[length -1]}") //o
        true
        true
        true
    }.apply {
        println("全部转成小写是:${toLowerCase()}") //derry you hao
    }

    println()

    // 普通写法
    val file = File("D:\\a.txt")
    file.setExecutable(true)
    file.setReadable(true)
    println(file.readLines())

    println()

    // apply写法
    // 匿名函数里面 持有的this == file本身
    /*val fileNew: File =*/ file.apply {
        setExecutable(true)
    }.apply {
        setReadable(true)
    }.apply {
        println(file.readLines())
    }
}

13.also、apply、let、run、with、takeIf、takeUnless

also:持有it,本身作为返回值
apply:持有this,本身作为返回值
let: 持有it,最后一行作为返回值
run:持有this,最后一行作为返回值
with:持有this,最后一行作为返回值
takeIf:持有it,如果为真返回本身,否则返回null
takeUnless:持有it,如果为假返回本身,否则返回null

fun main(args: Array<String>) {
// 普通方式 对集合第一个元素相加
    val list = listOf(6, 5, 2, 3, 5, 7)
    val value1 = list.first() // 第一个元素
    val result1 = value1 + value1
    println(result1)//12

    // let方式 对集合第一个元素相加
    val result2 = listOf(6, 5, 2, 3, 5, 7).let {
        // it == list集合
        it.first() + it.first() // 匿名函数的最后一行,作为返回值,let的特点,   但是前面学的apply永远是返回info本身
        /*true
        true
        true*/
    }
    println(result2)//12

    println()

    // 普通方式 对值判null,并返回
    println(getMethod1(/*null*/ "Derry"))//欢迎回来Derry非常欢迎

    // let方式 + 空合并操作符 对值判null,并返回
    println(getMethod3(/*null*/ "Derry"))//欢迎回来Derry非常欢迎
}

// 普通方式 对值判null,并返回
fun getMethod1(value: String?) : String {
    return if (value == null) "你传递的内容是null,你在搞什么飞机" else "欢迎回来${value}非常欢迎"
}
// 普通方式 简化版本
fun getMethod2(value: String?) = if (value == null) "你传递的内容是null,你在搞什么飞机" else "欢迎回来${value}非常欢迎"

// let方式 + 空合并操作符 对值判null,并返回
fun getMethod3(value: String?) : String {
    return value?.let {
        "欢迎回来${it}非常欢迎"
    } ?: "你传递的内容是null,你在搞什么飞机"
}

// let方式 + 空合并操作符 对值判null,并返回 简化版本
fun getMethod4(value: String?) =
    value?.let {
        "欢迎回来${it}非常欢迎"
    } ?: "你传递的内容是null,你在搞什么飞机"

14.run

特点:持有this,最后一行作为返回值

fun main() {
    val str = "Derry is OK"
    val r1 : Float = str.run {
        // this == str本身
        true
        5435.5f
    }
    println(r1)

    // 下面是 具名函数 配合 run函数
    str
        .run(::isLong) // this == str本身
        .run(::showText) // this == isLong返回的boolean值
        .run(::mapText)
        .run(::println)

    println()

    // let函数持有it,run函数持有this 都可以很灵活的,把上一个结果值 自动给 下一个函数
    str.let(::isLong) // it == str本身
    .let(::showText) // it == isLong返回的boolean值
    .let(::mapText) // it == str本身
    .let(::println) // it == str本身

    println()

    // >>>>>>>>>>>>>>>>>>>>>> 上面全部都是具名函数调用给run执行  下面全部是 匿名函数调用给run执行
    str
        .run {
            if (length > 5) true else false
        }
        .run {
            if (this) "你的字符串合格" else "你的字符串不合格"
        }
        .run {
            "【$this】"
        }
        .run {
            println(this)
        }
}

fun isLong(str: String) /* : Boolean */ = if (str.length > 5) true else false

fun showText(isLong: Boolean) /*: String */ = if (isLong) "你的字符串合格" else "你的字符串不合格"

fun mapText(getShow: String) /*: String */ = "【$getShow】"

15.with

特点同run,持有this,最后一行作为返回值

fun main() {
    val str = "李元霸"

    // 具名操作
    /*with(str) {
        this == str本身
    }*/
    val r1 = with(str, ::getStrLen)//3
    val r2 = with(r1, ::getLenInfo)//你的字符串长度是:3
    val r3 = with(r2, ::getInfoMap)//【你的字符串长度是:3】
    with(r3, ::show)//【你的字符串长度是:3】

    println()

    // 匿名操作
    with(with(with(with(str) {
        length
    }) {
        "字符串长度是:$this"
    }){
        "【$this】"
    }){
        println(this)//【字符串长度是:3】
    }
}

fun getStrLen(str: String) = str.length
fun getLenInfo(len: Int) = "你的字符串长度是:$len"
fun getInfoMap(info: String) = "【$info】"
fun show(content: String) = println(content)

16.also

持有it,本身作为返回值。类似apply,但apply持有this

fun main() {
    val str = "ABC"

    // 真正使用also函数的写法规则如下:
    // str.also特点:also函数始终是返回 “str本身”,所以可以链式调用
    str.also {
        println("str的原始数据是:$it") //ABC
    }.also {
        println("str转换小写的效果是:${it.toLowerCase()}") //abc
    }.also {
        println("结束了")
    }

17.takeIf

持有it,如果为真返回本身,否则返回null

fun main() {
       // name.takeIf { true/false }
    // true: 直接返回name本身
    // false: 直接返回null

    // 真正的用途
    println(checkPermissionAction2("Root", "!@#$"))

    // 小结:一般大部分情况下,都是 takeIf + 空合并操作符 = 一起使用
}

// 前端
public fun checkPermissionAction(name: String, pwd: String) : String? {
    return name.takeIf { permissionSystem(name, pwd) }
}

// takeIf + 空合并操作符
public fun checkPermissionAction2(name: String, pwd: String) : String {
    return name.takeIf { permissionSystem(name, pwd) } ?: "你的权限不够"
}

// 权限系统
private fun permissionSystem(username: String, userpwd: String) : Boolean {
    return if (username == "Root" && userpwd == "!@#$") true  else false
}

17.takeUnless

持有it,如果为假返回本身,否则返回null

class Manager {

    private var infoValue: String? = null

    fun getInfoValue() /* : String? */ = infoValue

    fun setInfoValue(infoValue: String) {
        this.infoValue = infoValue
    }
}

fun main() {
    val manager = Manager()

    /*
    "Derry".takeIf { *//*it == "Derry"*//* }
    "Derry".takeUnless { *//*it == "Derry"*//* }
    */

    // manager.setInfoValue("AAA")

    // 小结:takeUnless+it.isNullOrBlank() 一起使用,可以验证字符串有没有初始化等功能
    val r  = manager.getInfoValue().takeUnless { it.isNullOrBlank() } ?: "未经过任何初始化值"
    println(r)
}

14.集合

1.listOf(),getOrNull,getOrElse;setOf(),elementAtOrElse,elementAtOrNull

fun main() {
    val list = listOf("Derry", "Zhangsan", "Lisi", "Wangwu")
	// 取值:
	1.list[0],内部用的get(0)
	2.list.get(0)
	3.list.getOrElse(4){"你越界了"} 
	4.list.getOrNull(4) ?: "你越界了哦"
	5.set.elementAt(0)
	6.set.elementAtOrElse(4){"你越界了"} 
	7.set.elementAtOrNull(4) ?: "你越界了哦"
  
  val set: Set<String> = setOf("lisi", "wangwu", "zhaoliu", "zhaoliu") //set可以去重
  // 取值:
  1.set没有[0]这样的用法
  2.set.elementAt(0)
  3.set.elementAtOrElse
  4.set.elementAtOrNull
}

2.mutableListOf跟listOf的相互转换

fun main() {
    // 1.可变的集合,可add,可remove,toList()/toSet()变为不可变集合
    val mutableList = mutableListOf("Derry", "Zhangsna", "Wangwu")
   
    // 2.不可变集合,不可add,不可remove,toMutableList()/toMutableSet()变为可变集合
    val list = listOf(123, 456, 789)
}

3.+=,-=跟removeIf

fun main() {
// 1.mutator += -= 操作
    val list : MutableList<String> = mutableListOf("Derry", "DerryAll", "DerryStr", "Zhangsan")
    list += "李四" // mutator的特性 +=  -+ 其实背后就是 运算符重载而已
    list += "王五"
    list -= "Derry"
    println(list) //[DerryAll, DerryStr, Zhangsan, 李四, 王五]

    // 2.removeIf
    // list.removeIf { true } // 如果是true 自动变量整个可变集合,进行一个元素一个元素的删除
    list.removeIf { it.contains("Derr") } // 过滤所有的元素,只要是有 Derr 的元素,就是true 删除
    println(list) //[Zhangsan, 李四, 王五]
}

3.list遍历

fun main() {
    val list = listOf(1, 2, 3, 4, 5, 6, 7)

    // 第一种 遍历方式:
    for (i in list) {
        print("$i  ") //1  2  3  4  5  6  7
    }

    println()

    // 第二种 遍历方式:
    list.forEach {
        // it == 每一个元素
        print("$it  ") //1  2  3  4  5  6  7
    }

    println()

    // 第三种 遍历方式:
    list.forEachIndexed { index, item ->
        print("下标:$index, 元素:$item    ") //下标:0, 元素:1    下标:1, 元素:2    下标:2, 元素:3    下标:3, 元素:4    下标:4, 元素:5    下标:5, 元素:6    下标:6, 元素:7
    }
}

4.解构语法过滤元素

// 1.集合配合解构语法
// 2.反编译看Java给三个变量赋值的代码
// 3.解构屏蔽接收值
fun main() {
    val list: List<String> = listOf("李元霸", "李小龙", "李连杰")

    val(value1, value2, value3) = list
    //val表示只读的,不能赋值;赋值可以使用var
    println("value1:$value1, value2:$value2, value3:$value3")

    // 用_可以不接收赋值,可以节约一点性能
    val(_ , n2, n3) = list
    // println(_) _不是变量名,是用来过滤解构赋值的,不接收赋值给我
    println("n2:$n2, n3:$n3")
}

5.list去重

  list.toSet()
  list.toSet().toList()
  list.distinct() //快捷函数,内部 list.toMutableSet().toList()

6.kotlin数组类型

 Kotlin语言中的各种数组类型,虽然是引用类型,背后可以编译成Java基本数据类型
    IntArray        intArrayOf
    DoubleArray     doubleArrayOf
    LongArray       longArrayOf
    ShortArray      shortArrayOf
    ByteArray       byteArrayOf
    FloatArray      floatArrayOf
    BooleanArray    booleanArrayOf
    Array<对象类型>           arrayOf         对象数组

// 2.elementAtOrElse elementAtOrNull
// 3.List集合转 数组:toIntArray,toDoubleArray等
// 4.arrayOf 		Array<File>

7.map

1.创建
val mMap1 = mapOf("Derry" to 534.4, "Kevin" to 454.5)
val mMap2 = mapOf(Pair("Derry", 545.4), Pair("Kevin", 664.4))
两种方式等价

2.读取
mMap["Derry"] //内部 mMap.get("Derry"),找不到会返回null,不会崩溃
mMap.getOrDefault("Derry", -1)
mMap.getOrElse("Derry") {-1}
mMap.getValue("Derry") //不推荐,找不到会崩溃

3.遍历
map.forEach {
  	// it 内容 每一个元素 (K 和 V)  每一个元素 (K 和 V)  每一个元素 (K 和 V)
    // it 类型  Map.Entry<String, Int>
    println("K:${it.key} V:${it.value}")
}

for (item in map) {
    println("key:${item.key} value:${item.value}")
}

修改key、value变量名
map.forEach { (k, v) ->
    println("key:$k, value:$v")
}

4.可变集合操作: += [] put
val map : MutableMap<String, Int> = mutableMapOf(Pair("Derry", 123), "Kevin" to 456, Pair("Dee", 789))
    // 下面是可变操作
    map += "AAA" to 111
    map -= "Kevin"
    map["CCC"] = 888 //等于map.put("DDD", 888),put 和 [] 等价的

 map.getOrPut("FFF") { 555 } //有就取FFF的值 ,没有就把555加进去

15.类

1.Bean类

class KtBase70 {
    var info = "abcdefg ok is success" //注意:如果是val,则只能取值,不能赋值
//    如果需要修改get,set方法
//    get() = field.capitalize() // 把首字母修改成大写
//    set(value) {
//        field = "**【$value】**"
//    }

 // 计算属性  下面这样写 get函数覆盖了 field 内容本身,number2就属于计算属性了
    val number2 : Int //由于get覆盖了field,此处不能赋值
        get() = (1..1000).shuffled().first() // 从1到1000取出随机值 返回给 getNumber2()函数
        为什么没有看到 number2 属性定义?
        答:因为属于 计算属性 的功能,根本在getNumber2函数里面,就没有用到 number2属性,所以 number2属性 失效了,无用了,以后用不到了

    // 防范竞态条件  当你调用可能为null或空值的成员时,就必须采用防范竞态条件
	var info: String ? = null // ""
    fun getShowInfo() : String {
        return info?.let {
            if (it.isBlank()) {
                "info你原来是空值,请检查代码..."
            } else {
                "最终info结果是:$it"
            }
        } ?: "info你原来是null,请检查代码..."
    }
    
}

fun main() {
    val ktBase70 = KtBase70()
    ktBase70.info = "gsofis"
    println(ktBase70.name)
}

2.构造函数

class KtBase72(_name: String) {
//构造函数的参数不能直接用,但在init代码块可以直接使用
init {//主构造被调用时调用
        println("主构造函数被调用了 $_username")
        // 要求...,否则...
        require(username.isNotBlank()) { "你的username空空如也,异常抛出" }
    }

//构造函数的参数不能直接用,需要重新赋值才能使用
var name = _name
//更好的方式:在参数前加上var或val,例如class KtBase72(var name: String) 

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

3.lateinit延迟初始化

lateinit var responseResultInfo: String
//为了转成 kproperty对象,才能获取,是否已经初始化了
使用 if (::responseResultInfo.isInitialized) 判断是否初始化
    
//普通方式(饿汉式 没有任何懒加载的特点)
 val databaseData1 = readSQlServerDatabaseAction()
//惰性加载
 val databaseData2 by lazy { readSQlServerDatabaseAction() }

4.open

 KT所有的类和方法,默认是final修饰的,不能被继承和重写,和Java相反。
 解决:在类和方法前面加open

5.is、as

is:是否是xxx实例
val p : Person3 = Student3("李四")
//if (p is Student3) {
//        p.methodStudent()//智能类型转换
//}

    (p as Student3).methodStudent()//这里已经转换了,所以下面可以调用子类的方法。否则不能调用
    p.methodStudent()

6.Any()

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

7.object单例

object KtBase87 {
    /* object 对象类背后做了什么事情
        public static final KtBase87 INSTANCE;

        private KtBase87() {} // 主构造废除一样的效果

        public final void show() {
            String var1 = "我是show函数...";
            ...
            System.out.println(var1);
        }

        // 这个区域是 object 不同点:
        static {
            KtBase87 var0 = new KtBase87();
            INSTANCE = var0;
            String var1 = "KtBase91 init...";
            ...
            System.out.println(var0);
        }
     */

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

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

8.对象表达式

interface RunnableKT {
    fun run()
}

open class KtBase88 {
    open fun add(info: String) = println("KtBase88 add:$info")
    open fun del(info: String) = println("KtBase88 del:$info")
}

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

fun main() {
// 具名实现方式
    val p2 = KtBase88Impl()
    p2.add("刘一")
    p2.del("刘二")

// 匿名对象 表达式方式
    val p = object : KtBase88() {
        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("李连杰")

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

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

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

    // 对KT的接口 用   Java最简洁的方式 方式二
    //kt没有这种方式
//    RunnableKT {
//
//    }

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

9.伴生对象

class KtBase89 {

    // 伴生对象
    companion object {
        val info = "DerryINfo"
        fun showInfo() = println("显示:$info")
        val name = "Derry"
    }

    /* companion object {} 背后的逻辑

       private static final String name = "Derry";
       private static final String info = "DerryINfo";
       public static final KtBase89.Companion Companion = new KtBase89.Companion(xxx);

       public static final class Companion {
           @NotNull
          public final String getInfo() {
             return KtBase89.info;
          }

          @NotNull
          public final String getName() {
             return KtBase89.name;
          }

          public final void showInfo() {
             String var1 = "显示:" + ((KtBase89.Companion)this).getInfo();
             boolean var2 = false;
             System.out.println(var1);
          }

          private Companion() {}

          // $FF: synthetic method
          public Companion(DefaultConstructorMarker $constructor_marker) {
             this();
          }
        }

     */
}

// 伴生对象的由来: 在KT中是没有Java的这种static静态,伴生很大程度上和Java的这种static静态 差不多的
// 伴生对象只会初始化一次
fun main() {
    // 背后代码:System.out.println(KtBase89.Companion.getInfo())
    println(KtBase89.info)
    // 背后代码:System.out.println(KtBase89.Companion.getName())
    println(KtBase89.name)
    // 背后代码:KtBase89.Companion.showInfo()
    KtBase89.showInfo()
}

10.嵌套类、内部类

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

// 内部类(inner class)的特点: 内部的类 能访问 外部的类
//              外部的类 能访问 内部的类

class Outer {
    val info: String  = "OK"
    fun show() {
        Nested().output()
    }

    class Nested {
        fun output() = println("嵌套类")
    }
}

class Body(_bodyInfo: String) { // 身体类
    val bodyInfo = _bodyInfo
    fun show() {
        Heart().run()
    }

    inner class Heart { // 心脏类
        fun run() = println("心脏访问身体信息:$bodyInfo")
    }
}

fun main() {
    // 嵌套类:
    Outer.Nested().output()
    // 内部类:
    Body("isOK").Heart().run()
}

11.普通类、数据类

// 普通类
class ResponseResultBean1(var msg: String, var code: Int, var data: String) : Any()
// set get 构造函数

// TODO 99-数据类使用条件
// 条件一:数据类至少必须有一个参数的主构造函数,参数必须有var或val修饰
// 条件二:数据类不能使用 abstract,open,sealed,inner 等等 修饰 (数据类,只是做数据载入和数据存储的事情)
// 条件三:需求:比较,copy,toString,解构,等等这些丰富的功能时,也可以使用数据类

data class ResponseResultBean2(var msg: String, var code: Int, var data: String) : Any()
// set get 构造函数 解构操作 copy 重写toString hashCode equals  数据类 生成 更丰富

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=登录成功的数据...)

== 值的比较,相当于java的equals.
println(
        ResponseResultBean1("loginSuccess", 200, "登录成功的数据...") ==
                ResponseResultBean1("loginSuccess", 200, "登录成功的数据...")
    )
    // false,Any父类的 equals 实现 (ResponseResultBean1对象引用 比较 ResponseResultBean1对象引用)


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

12.copy函数

data class KtBase92 (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 "KtBase92(name=" + this.name + ", age=" + this.age + ")";
    }
 */

fun main() {
    val p1 = KtBase92("李元霸") // 调用次构造初始化对象
    println(p1)
//主构造被调用了
//次构造被调用
//toString name:李元霸, age:99, coreInfo:增加非常核心的内容信息
 
    val newP2 = p1.copy("李连杰", 78)
    println(newP2)
//主构造被调用了
//toString name:李连杰, age:78, coreInfo:

    // copy toString hashCode equals 等等... 主管主构造,不管次构造
    // 注意事项:使用copy的时候,由于内部代码只处理主构造,所以必须考虑次构造的内容
}

13.解构

// 普通类
class Student1(var name: String , var age: Int, var sex: Char) {
    // 注意事项:不能是component0,顺序必须是 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")//普通类 结构后:name:李四, age:89, sex:男

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

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

14.运算符重载

class AddClass1(number1: Int, number2: Int)

// 写一个数据类,就是为了,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, 2) + AddClass2(3, 4))
}

15.枚举类

// KT想表达:枚举其实也是一个class,为什么,就是为了 枚举可以有更丰富的功能
enum class Week {
    星期一,//枚举主构造参数必须跟枚举参数一致
    星期二,
    星期三,
    星期四,
    星期五,
    星期六,
    星期日;//分号表示结束枚举
}
//======================================================
// 四肢信息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)); // 右脚,分号表示结束枚举

    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}")
    }
}
//====================================================
代数类型数据
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 其他,所以不需要写
        }
}


// TODO 95-Kotlin语言的枚举类学习
fun main() {
    println(Week.星期一)//星期一
    // 枚举的值 等价于 枚举本身
    println(Week.星期二 is Week)//true

// 一般的用法如下:
    println(Limbs.LEFT_HAND.show())
    Limbs.RIGHT_HAND.updateData(LimbsInfo("右手2", 99))

println(Teacher(Exam.Fraction1).show())
}

16.密封类

// 密封类,成员必须有类型,并且继承本类
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}"
        }
}

// TODO 98-Kotlin语言的密封类学习
fun main() {
    println(Teachers(Exams.Fraction1).show())
    println(Teachers(Exams.Fraction4("王五")).show()) // 对象2

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

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

16.接口

1.接口定义

// TODO 100-Kotlin语言的接口定义
// 1.接口里面的所有成员 和 接口本身 都是 public open 的,所以不需要open,这个是接口的特殊
// 2.接口不能有主构造,反正就是没有构造
// 3.实现类要重写接口函数和接口成员,且要加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()) //Mouse USB 3.0, 鼠标接入了UBS口

    println()

    val iusb2: IUSB = KeyBoard()
    println(iusb2.insertUBS()) 
    //@你get了[键盘接入了UBS口]值出去了
	//KeyBoard USB 3.1, 键盘接入了UBS口

    iusb2.usbInsertDevice = "AAA" //@你set了[AAA]值进来了
}

2.修改接口的默认实现

interface USB2 {
    // 1.接口 var 是不能直接给接口成员赋值的 (但是有其他办法get())
    // 2.任何类 接口 等等  val 代表只读的,是不可以在后面动态赋值 (也有其他办法)
    
// TODO 101-Kotlin语言的接口的默认实现
// 1.注意:这样做是不建议的,因为接口成员本来就是用来声明标准的
//        但是可以在接口成员声明时,完成对接口成员的实现

    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"
}


fun main() {
    val iusb1 = Mouse2()
    println(iusb1.insertUBS()) //Mouse 70, 高级设备接入USB,随机打印
}

3.抽象类

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 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()
    }
}

// TODO 102-Kotlin语言的抽象类学习
fun main() = LoginActivity().show()
//加载{564}布局xml中
//做具体初始化View的实现
//做具体初始化数据的实现
//做具体初始化XXX的实现

4.泛型

  1. Java SE 1.5的才有的特性,本质是参数化类型,泛型分为泛型类泛型接口泛型方法
    在没有泛型的情况的下只能通过对Object 的引用来实现参数的任意化
    没泛型的缺点:要显式的强制类型转换,而强制转换在编译期是不做检查的,容易把问题留到运行时
    泛型优点:在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率,避免在运行时出现 ClassCastException

  2. 泛型实现原理:泛型擦除
    编译器在编译期对字节码时先进行类型检查,再进行类型擦除所有泛型类型相关信息(即所有类型参数都用限定类型替换,包括类、变量和方法。如果类型变量有限定则原始类型就用第一个边界的类型来替换,譬如 class Test<T extends Comparable & Serializable> {} 的原始类型就是 Comparable),即运行时就不存在任何泛型类型相关的信息

    fun test() {
       	 val mList= ArrayList<String>() //List<Integer> 在运行时仅用一个 List 来表示,这样做的目的是为了和 Java 1.5 之前版本进行兼容
           mList.add("123") //实际上是"123"作为Object存入集合中的
           Log.v("tag",mList[0]) //从list实例中读取出来Object然后转换成String之后才能使用
       }
    
  3. inline
    inline函数中我们可以指定类型不被擦除, 因为inline函数在编译期会将字节码copy到调用它的方法里,所以编译器会知道当前的方法中泛型对应的具体类型是什么,然后把泛型替换为具体类型,从而达到不被擦除的目的,在inline函数中我们可以通过reified关键字来标记这个泛型在编译时替换成具体类型。

    思考:我们在用Gson解析json数据的时候,是如何解析数据拿到泛型类型 Bean 结构的?
    TypeToken 是一种方案,可以通过getType() 方法获取到我们使用的泛型类的泛型参数类型,不过采用反射解析的时候,Gson构造对象实例时调用的是默认无参构造方法,所以依赖 Java 的 Class 字节码中存储的泛型参数信息,Java 的泛型机制虽然在编译期间进行了擦除,但是Java 在编译时会在字节码里指令集以外的地方保留部分泛型的信息,接口、类、方法定义上的所有泛型、成员变量声明处的泛型都会被保留类型信息,其他地方的泛型信息都会被擦除,这些信息被保存在 class 字节码的常量池中,使用泛型的代码处会生成一个 signature 签名字段,通过签名 signature 字段指明这个常量池的地址,JDK 提供了方法去读取这些泛型信息的方法,利用反射就可以获得泛型参数的具体类型,譬如:

    (mList.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
    

    一般json解析

    inline fun <reified T> Gson.fromJson(jsonStr: String) = fromJson(json, T::class.java)
    
  4. reified
    在inline函数中通过reified关键字来标记这个泛型在编译时替换成具体类型

  5. 泛型使用示例
    Kotlin的泛型类型推断功能还有一种最有用的地方,就是根据不同泛型类型调用不同的方法这种场景

        //bundle 存值
        fun <T> putBundleValue(key: String, value: T) {
            when (value) {
                is Int -> bundle.putInt(key, value)
                is Long -> bundle.putLong(key, value)
                is String -> bundle.putString(key, value)
                is Boolean -> bundle.putBoolean(key, value)
                else -> throw  Exception("不支持的类型")
            }
        }
     
     
        //bundle 取值
        inline fun <reified T> getBundleValue(key: String): T {
            return when (T::class.java) {
                java.lang.Integer::class.java -> bundle.getInt(key) as T
                java.lang.Long::class.java -> bundle.getLong(key) as T
                java.lang.String::class.java -> bundle.getString(key) as T
                java.lang.Boolean::class.java -> bundle.getBoolean(key) as T
     
    //     注意不能写成以下形式,否则匹配不到类型,因为 reified 会推断为Java.lang包下的基本类型而不是kotlin包下的类型
    //     Int::class.java -> bundle.getInt(key) as T
    //     Long::class.java -> bundle.getLong(key) as T
    //     String::class.java -> bundle.getString(key) as T
    //     Boolean::class.java -> bundle.getBoolean(key) as T
                
                else -> throw  Exception("不支持的类型")
            }
        }
    

    这样只用一个函数就能调用存取数据方便多了

    putValue("string", "hello") 
    putValue("bool", true)
           
    val stringValue=getValue<String>("string")
    val booleanValue:Boolean = getValue("bool") //声明类型后泛型能自动推导
    

    补充一下: 如果限定泛型T为基本类型,其实也能这样投机取巧来实现

      //bundle 取值
      inline fun <reified T> getBundleValue(key: String): T {
            return when {
                0 is T -> bundle.getInt(key) as T  //判断T是否为Int
                1L is T-> bundle.getLong(key) as T //判断T是否为Long
                "h" is T -> bundle.getString(key) as T //判断T是否为String类型
                true is T -> bundle.getBoolean(key) as T //判断T是否为Boolean类型
     
                else -> throw  Exception("不支持的类型")
            }
        }
    
class KtBase103<T> (private val obj: T) { // 万能输出器
    fun show() = println("万能输出器:$obj")
}

// TODO 103-Kotlin语言的定义泛型类
// 1.定义 对象输出器 println(obj)
// 2.定义两个对象,三个属性
// 3.对象 String Int Double Float Char 等 测试 对象输出器

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

fun main() {
    val stu1 = Student("张三", 88, '男')
    val tea1 = Teacher("王五", 77, '男')

    KtBase103(stu1).show() //万能输出器:Student(name=张三, age=88, sex=男)
    KtBase103(tea1).show() //万能输出器:Teacher(name=王五, age=77, sex=男)

    KtBase103(String("刘一".toByteArray())).show() //万能输出器:刘一

    KtBase103(575).show() //万能输出器:575
    KtBase103(53456.45).show() //万能输出器:53456.45
    KtBase103(4645.5f).show() //万能输出器:4645.5
    KtBase103('男').show() //万能输出器:男
}

1.泛型类型变换

// 1.万能对象返回器 Boolean来控制是否返回 运用 takeIf
class KtBase104<T>(private val isTrue: Boolean, private val obj: T) {
    fun getObj() : T? = obj.takeIf { isTrue }
}
data class Teacher(val name: String , val age: Int, val sex: Char)

// TODO 104-Kotlin语言的泛型函数学习
// 1.万能对象返回器 Boolean来控制是否返回 运用 takeIf
// 2.四个对象打印
// 3.对象打印 + run + ?:
// 4.对象打印 + apply + ?:
// 5.show(t: T) + apply + ?:
fun main() {
    val tea1 = Teacher("王五", 77, '男')

    println(KtBase104(true, tea1).getObj()) //Teacher(name=王五, age=77, sex=男)
    println(KtBase104(false, tea1).getObj() ?: "大哥,你万能对象返回器,是返回null啊") //大哥,你万能对象返回器,是返回null啊

    println()

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

    println()

    // apply特点:永远都是返回 getObj.apply  getObj本身
    val r2 : Teacher = KtBase104(true, tea1).getObj().apply {  }!!
    println("r2:$r2") //r2:Student(name=王五, age=77, sex=男)

    // 4.对象打印 + apply + ?:
    val r3: Teacher = KtBase104(true, tea1).getObj() .apply {
        // this == getObj本身
        if (this == null) {
            println("r3大哥,你万能对象返回器,是返回null啊")
        } else {
            println("r3万能对象是:$this")
        }
    }!!
    println("r3:$r3")
    //r3万能对象是:Teacher(name=王五, age=77, sex=男)
	//r3:Teacher(name=王五, age=77, sex=男)

    println()

    show("Derry") //show万能对象是:Derry
    show(null) //show,你万能对象返回器,是返回null啊

    println()

    show2("Derry")
    //show2,万能对象是:Derry
	//show2: 你传递进来的r:Derry
	
    show2(null) //show2: 你传递进来的r:null
}

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

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

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

    println("show2: 你传递进来的r:$r")
}

2.泛型配合lambda使用

// 1.类 isMap map takeIf  map是什么类型
class KtBase105<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

// TODO 105-Kotlin语言的泛型变换实战
// 1.类 isMap map takeIf  map是什么类型
// 2.map int -> str 最终接收是什么类型
// 3.map per -> stu 最终接收是什么类型
// 4.验证是否是此类型 与 null
fun main() {

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

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

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

    println()

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

    println()

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

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

3.泛型类型约束<T : PersonClass>

open class MyAnyClass(name: String) // 祖宗类 顶级父类
open class PersonClass(name: String) : MyAnyClass(name = name) // 父类
class StudentClass(name: String) : PersonClass(name = name) // 子类
class DogClass(name: String) // 其他类 另类

// TODO 106-Kotlin语言的泛型类型约束学习
// T : PersonClass相当于Java的 T extends 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("Derry1")// 祖宗类 顶级父类
    val dog = DogClass("Derry1") // 其他类 另类
    val per = PersonClass("Derry1") // 父类
    val stu = StudentClass("Derry1") // 子类

//    val r1 = KtBase106(any).getObj() // 提示报错了,因为类型限定了传入的参数只能是PersonClass及其子类
//    println(r1)

//    val r5 = KtBase106(dog).getObj() // 提示报错了,因为类型限定了传入的参数只能是PersonClass及其子类
//    println(r5)

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

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

4.vararg多参数

// TODO 107-Kotlin语言的vararg关键字(动态参数)
// 1.objectArray:Array<T>
// 2.showObj(index)
// 3.mapObj(index,变换lambda)
// 4.p.showOBj  p.mapObj(int -> str)
// 5.p的类型  it的类型
class KtBase107<T> (vararg objects : T, var isMap: Boolean) {
    // out 我们的T只能被读取,不能修改   T只能读取
    private val objectArray : Array<out T> = objects

    // 2.showObj(index)  "你${index}下标去的对象是null"
    fun showObj(index: Int) : T? = objectArray[index].takeIf { isMap } ?: null /*objectArray[index]*/

    // 3.mapObj(index, 变换lambda)   objectArray[index]
    fun <O> mapObj(index: Int, mapAction: (T?) -> O) = mapAction( objectArray[index].takeIf { isMap }  /*objectArray[index]*/ )
}


fun main() {
    val p2 : KtBase107<String> = KtBase107("AAA", "BBB", "CCC", isMap = true)
    // 为什么 it 是 String ? , 是因为你的  lambda (T ?) -> O  T? 指定了 ?
    val r3 = p2.mapObj(2) {
        it
        // it 是什么类型 ?  String ?
        "我要把你变成String类型 it:$it"
    }
    println(r3) //我要把你变成String类型 it:CCC

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    //  由于使用了太多类型混合,所以真正类型是 : KtBase107<{Comparable<*> & java.io.Serializable}>
    //  由于不允许我们这样写,所以我们用父类 Any? 代替
    val p : KtBase107<Any?>  = KtBase107("Derry", false, 53454, 4543.3f, 4554.54, null, 'C', isMap = true)

    println(p.showObj(0))//Derry
    println(p.showObj(1))//false
    println(p.showObj(2))//53454
    println(p.showObj(3))//4543.3
    println(p.showObj(4)) // 4554.54
    println(p.showObj(5)/*?.特殊操作 如果是null 会引发奔溃*/) // null
    println(p.showObj(6)) // C

    println()

    // mapObj
    // it类型 {Comparable<*> & java.io.Serializable}  需要转换一下才行 例如:it.toString
    val r : Int = p.mapObj(0) {
        it
        it.toString()
        it.toString().length
    }
    println("第零个元素的字符串长度是:$r")//第零个元素的字符串长度是:5

    // it类型 {Comparable<*> & java.io.Serializable}  由于我们的第三个元素是 Int类型,所以不需要转换,自动转的
    val r2 : String = p.mapObj(2) {
        it
        "我的第三个元素是:$it"
    }
    println(r2)//我的第三个元素是:53454
}

5.运算符[]重载

class KtBase108 <INPUT> (vararg objects: INPUT, val isR: Boolean = true) {
    // 开启INPUT泛型的只读模式
    private val objectArray: Array<out INPUT> = objects

    // 运算符重载
    operator fun get(index: Int) : INPUT ? = objectArray[index].takeIf { isR }
}

fun <INPUT> inputObj(item: INPUT) {
    // 小结:异步处理泛型接收,都用 String?,如果用String可能为空
    println((item as String?)?.length ?: "你个货传递的泛型数据是null啊")
}

// TODO 108-Kotlin语言的[ ]操作符学习
// 1.给泛型传入null后,直接操作
fun main() {
    inputObj(null) //你个货传递的泛型数据是null啊

    println()

    // 只要有一个元素是null,那么所有的元素都是 String?
    val p1 : KtBase108<String?> = KtBase108("张三", "李四", "王五", null)

    var r : String? = p1[0]

    println(r) //张三
    println(p1[1]) //李四
}

6.协变out,逆变in

//特点:
//协变out T,作为返回值,只能被读取。相当于? extends T,out 父类 = 子类
//默认情况下,泛型的子类对象不可以赋值给泛型的父类对象,加上out可以
//例如List<CharSequence> list1 = new ArrayList<String>()会编译不过,需要List<? extends CharSequence> list2 = new ArrayList<String>();

//逆变in T,作为参数,只能被修改。相当于? super T,in  子类 = 父类
//默认情况下,泛型的父类对象不可以赋值给泛型的子类对象,加上in可以
//例如List<String> list1 = new ArrayList<CharSequence>()会编译不过,需要List<? super String> list2 = new ArrayList<CharSequence>();

// 生产者, 协变 out T 代表整个生产者类里面这个T只能被读取,不能被修改了
interface Producer<out T> {
    fun producer() : T

    // 不能被修改了 (编译不通过)
    // fun consumer(itme: T)  /*{  消费代码  }*/
}

// 消费者 in T  逆变 [in T 此泛型只能被修改 更新 所以是in]
interface Consumer <in T> {
//    in T  代表整个生产者类里面  这个T  只能被读取,不能被修改了

    fun consumer(itme : T) /*{  消费代码  }*/

    // 不能被读取 (编译不通过)
    // fun producer() : T
}

// 生产者&消费者 T  默认情况下,是不变
interface ProducerAndConsumer<T> {
    // 能被修改了
    fun consumer(itme : T) /*{  消费代码  }*/

    // 能被读取
    fun producer() : T
}

open class Animal // 动物
open class Humanity : Animal() // 人类
open class Man : Humanity() // 男人
open class WoMan : Humanity() // 女人

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  只管生产者
class ProducerClass1 : Producer<Animal> {
    override fun producer(): Animal {
        println("生产者 Animal")
        return Animal()
    }
}

class ProducerClass2 : Producer<Humanity> {
    override fun producer(): Humanity {
        println("生产者 Humanity")
        return Humanity()
    }
}

class ProducerClass3 : Producer<Man> {
    override fun producer(): Man {
        println("生产者 Man")
        return Man()
    }
}

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  只管消费者
class ConsumerClass1 : Consumer<Animal> {
    override fun consumer(item: Animal) {
        println("消费者 Animal")
    }
}

class ConsumerClass2 : Consumer<Humanity> {
    override fun consumer(item: Humanity) {
        println("消费者 Humanity")
    }
}

fun main() {
	val p1 : Producer<Animal> = ProducerClass1() // ProducerClass1他本来就是 传递 Animal ,当然是可以的
    val p2 : Producer<Animal> = ProducerClass2() // ProducerClass2他本来就是 传递 Humanity,居然也可以,因为out
    val p3 : Producer<Animal> = ProducerClass3() // ProducerClass3他本来就是 传递 Man,居然也可以,因为out

	val p1 : Consumer<Man> = ConsumerClass1() // ConsumerClass1他本来就是 传递 Animal,居然也可以,因为in
    val p2 : Consumer<WoMan> = ConsumerClass2() // ConsumerClass1他本来就是 传递 Humanity,居然也可以,因为in
}

7.reified示例

// 1.定义3个Obj类
data class ObjectClass1(val name: String, val age: Int, val study: String)
data class ObjectClass2(val name: String, val age: Int, val study: String)
data class ObjectClass3(val name: String, val age: Int, val study: String)

class KtBase112 {
    // 所有的功能,写在函数上
    // 默认随机输出一个对象,如果此对象和用户指定的对象不一致,我们就启用备用对象,否则就直接返回对象
    inline fun <reified T> randomOrDefault(defaultLambdaAction: () -> T ) :T? {
        val objList : List<Any> = listOf(ObjectClass1("obj1 李四", 22, "学习C"),
                                         ObjectClass2("obj2 王五", 23, "学习C++"),
                                         ObjectClass3("obj3 赵六", 24, "学习C#"))

        val randomObj : Any? = objList.shuffled().first()
        println("您随机产生的对象 幸运儿是:$randomObj")
        // return randomObj.takeIf { it is T } as T ?: null     :T? {

        // T  与  T?  是不同的
        // 答: it is T false  takeIf  null    null as T 奔溃了,解决思路: null as T?

        // 如果  it随机产生的对象 等于 T类型的,就会走 as T 直接返回了
        return randomObj.takeIf { it is T } as T?  // null as T     null as T?
            // 如果  it随机产生的对象 不等于 T类型的,就会走下面这个备用环节
            ?: defaultLambdaAction()
    }
}

// TODO 112-Kotlin语言的reified关键字学习
// 1.定义3个Obj类
// 2.randomOrDefault函数 备用机制的lambda
// 3.lists.shuffled()
fun main() {
    val finalResult = KtBase112().randomOrDefault<ObjectClass1> {
        println("由于随机产生的对象 和 我们指定的ObjectClass1不一致,所以启用备用对象")
        ObjectClass1("备用 obj1 李四", 22, "学习C") // 最后一行的返回
    }
    println("客户端最终结果:$finalResult")
}

17.扩展函数

//注意点:
// 第一点:如果我们自己写了两个一样的扩展函数,编译不通过
// 第二点:KT内置的扩展函数,被我们重复定义,属于覆盖,而且优先使用我们自己定义的扩展函数

class KtBase113 (val name: String, val age: Int, val sex: Char)

// 增加扩展函数
fun KtBase113.show() {
    println("我是show函数, name:${name}, age:$age, sex:$sex")
}
fun KtBase113.getInfo() = "我是getInfo函数, name:${name}, age:$age, sex:$sex"
fun String.addExtAction(number: Int) =  this + "@".repeat(number) //this是String对象本身
fun <T> T.showContentInfo() = println("${if (this is String) "你的字符串长度是:$length" else "你不是字符串 你的内容是:$this"}")
fun <INPUTTYPE> INPUTTYPE.showTypesAction() =
    when(this) {
        is String -> "原来你是String类型"
        is Int -> "原来你是Int类型"
        is Char -> "原来你是Char类型"
        is Float -> "原来你是Float类型"
        is Double -> "原来你是Double类型"
        is Boolean -> "原来你是Boolean类型"
        is Unit -> "原来你是无参返回函数类型"
        else -> "未知类型"
    }
    // private 私有化
// inline  我们的函数是高阶函数,所以用到内联,做lambda的优化,性能提高
// fun<I, O> 在函数中,申明两个泛型,函数泛型  I输入Input, O输出Output
// I.mLet 对I输入Input进行函数扩展,扩展函数的名称是 mLet,意味着,所有的类型,万能类型,都可以用 xxx.mLet
// lambda : (I) -> O   (I输入参数) -> O输出
//  : O  会根据用户的返回类型,变化而变化
// lambda(this) I进行函数扩展,在整个扩展函数里面,this == I本身
private inline fun<I, O> I.mLet(lambda : (I) -> O) : O = lambda(this)

// INPUT.() -> Unit 让我们的匿名函数里面持有 this ,在lambda里面不需要返回值,因为永远都是返回INPUT本身
// lambda(this) 默认就有this
// 返回this的目的是可以链式调用
private inline fun <INPUT> INPUT.mApply(lambda : INPUT.() -> Unit) : INPUT  {
    lambda() // 省略this
    return this
}

//函数也可以作为T泛型
fun commonFun() {}
fun commonFun2() = "DDD"

// xxx.yyy()  函数里的this是xxx对象本身
// TODO 113-Kotlin语言的定义扩展函数学习
fun main() {
    val p = KtBase113("张三", 28, '男')
    p.show() //我是show函数, name:张三, age:28, sex:男
    println(p.getInfo()) //我是getInfo函数, name:张三, age:28, sex:男
    println("Kevin".addExtAction(3)) //Kevin@@@
	println(commonFun().showTypesAction()) //原来你是无参返回函数类型
    println(commonFun2().showTypesAction()) //原来你是String类型
    
    // 万能类型,任何类型,所有类型,都可以使用我的 mLet
    val r2 : String = "Derry2".let {
        it
        34543.45f
        'A'
    }
    
val r : File = File("D:\\a.txt")
        .mApply {
            // 输入的是 this == File对象本身
            setReadable(true)
            setWritable(true)
            println("1 ${readLines()}")
        }.mApply {
            // 输入的是 this == File对象本身
            setReadable(true)
            setWritable(true)
            println("2 ${readLines()}")
        }.mApply {
            // 输入的是 this == File对象本身
            setReadable(true)
            setWritable(true)
            println("3 ${readLines()}")
        }
}

18扩展属性

// val String.myInfo: String = "AAA",扩展属性不能直接复制,需要重写get方法
val String.myInfo: String
    get() = "Derry"

19.扩展文件及扩展函数重命名

KtBase120Ext.kt
// 1.扩展文件一般都是public,如果private外界无法使用
// 2.Iterable<E> 的子类 set list 都可以用,所以用父类
// 3.本次扩展函数的作用是,随机取第一个元素返回
fun <T> Iterable<T>.randomItemValuePrintln() = println(this.shuffled().first())

// 导入扩展文件
// 在工作中非常有用,可以把很多的扩展操作,写到某一个地方,到时候引入过来用,比较独立化
import com.derry.s6.com.derry.randomItemValuePrintln
import com.derry.s6.com.derry.randomItemValuePrintln as p // as p 重命名扩展操作

// TODO 120-Kotlin语言的定义扩展文件
fun main() {
    val list : List<String> = listOf("李元霸", "李连杰", "李小龙")
    val set : Set<Double> = setOf(545.5, 434.5, 656.6)

    // 使用 扩展文件
    list.randomItemValuePrintln()
    set.randomItemValuePrintln()
    list.p()
    set.p()
}

19.infix中缀函数

使用条件:用于成员方法或者扩展方法,且只有一个参数

//用于扩展方法
val map = mapOf(1 to "one", 2 to "two", 3 to "three")

infix fun Int.add(x: Int): Int {
    return this + x
}
println(100 add 200) //300

private infix fun <C1, C2> C1.gogogo(c2: C2) {
    println("中缀表达式,C1是: $this, C2是: $c2")
}

"Derry2" gogogo 'M' //中缀表达式,C1是: Derry2, C2是: M

//用于成员方法
class Account {
    var balance = 100.0

    infix fun add(amount: Double) : Unit {
        this.balance = balance + amount
    }
}

val  account = Account()
account add 100.00
print(account.balance) //200
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值