Kotlin基础

Attention:

  • 本文章适用于有编程基础的人,如果有多种编程语言基础,例如Swift,会发现很多相似之处,学习起来也会比较容易
  • 本文章属于笔记,没有知识讲解条理,不太适合透彻学习Kotlin,纯属写代码时的参考

一、基础


基本内置数据类型

  • String
  • Char
  • Boolean --> Java boolean
  • Int --> Java int
  • Double --> Java double
  • Float --> Java float
  • List
  • Set
  • Map
    Kotlin中一些引用类型编译器会转化为Java的基本类型,不会有额外的性能消耗

编译时常量

const val PI = 3.1415
  • 编译时常量只能是常用的基本数据类型:(String, Double, Int, Float, Long, Short, Byte, Char, Boolean)
  • 编译时常量只能定义在函数外(如果在函数内定义的话就必须在运行时才能调用函数赋值,就不能称是编译时常量了,所以编译时常量只能定义在函数之外,这样就能在编译期间就初始化了)

range表达式

image.png

when表达式

表达式有返回值,语句没有返回值

    val week = 5
    val info = when(week) {
        1 -> "今天是星期1"
        2 -> "今天是星期2"
        3 -> "今天是星期3"
        4 -> "今天是星期4"
        5 -> "今天是星期5"
//        6 -> true        // when返回Any类型
        else -> "今天是周末" // when返回String类型
//        else -> {        // when返回Any类型
//            println("Any")
//        }
    }
    println(info)

String模版

    val park = "黄石公园"
    val time = 6
    println("今天去${park}玩了$time 个小时") // $变量名 后面没有字符串(标点符号、空格可以)的可以不加{}

    val isLogin = true
    println("${if (isLogin) "登录成功" else "登录失败,请重试"}")

函数

image.png

函数参数的默认值

fun action1(name: String, age: Int) {
    println("姓名;$name, 年龄:$age")
}

fun action2(name: String, age: Int = 88) {
    println("姓名;$name, 年龄:$age")
}

具名函数参数

fun main() {
    loginAction(age = 30, name = "test", phone = "12345", pwd = "123")
}

fun loginAction(name: String, pwd: String, age: Int, phone: String) {
    println("name: $name, pwd: $pwd, age: $age, phone: $phone")
}

Unit函数特点

image.png

Nothing类型特点

TODO() // 终止程序继续执行,抛出一个异常

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("优秀")
    }
}

带反引号函数名特点

fun main() {
    // 第一种情况:函数特殊命名
    `登录功能测试 需求实现人张三`("test", "pwd")

    // 第二种情况:函数命名使用了Kotlin中的关键词
    JavaTest.`is`()
    JavaTest.`in`()

    // 第三种情况:函数名违反命名规则,反编译时导致崩溃防止反编译
    `655435383`()
}

private fun `655435383`() {
}

// 文档中注释
// 防止反编译的函数`655435383`,作用是xxx

二、语法糖


匿名函数

fun main() {
    val len = "test".count()
    println(len) // 4
    
    val len2 = "test".count { 
        it == 't'
    }
    println(len2) // 2
}

匿名函数-函数类型&隐式返回

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

    // 2.对上面声明函数的实现
    methodAction = {
        val name = "name"
        "返回值:$name" //最后一行就是函数的返回值
    }

    // 3.调用
    println(methodAction())
}

匿名函数-函数参数

fun main() {
    val methodAction: (Int, Int, Int) -> String = { number1, number2, number3 ->
        val name = "name"
        "$name: $number1, $number2, $number3"
    }
    println(methodAction(10, 20, 30))
}

匿名函数-it关键字

匿名函数的参数只有一个的话,会自动生成参数名it,代表被传入的参数对象

fun main() {
    val methodAction: (String) -> String = {
        "$it"
    }
    println(methodAction("name"))
    
    val methodAction2: (Double) -> String = {
        "$it"
    }
    println(methodAction2(123.4))
}

匿名函数-类型推断

fun main() {
    val method1 = { v1: Double, v2: Float, v3: Int ->
        "v1:$v1, v2:$v2, v3:$v3"
    } // (Double, Float, Int) -> String
    println(method1(12.3, 45.6f, 10))

    val method2 = {
        123.0f
    } // () -> Unit
    println(method2())

    val method3 = { number: Int ->
        number
    } // (Int) -> Int
    println(method3(1))
}

在函数中定义参数是函数的参数

fun main() {
    loginApi("name", "pwdd") { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    }
}

const val USER_NAME = "name"
const val USER_PWD = "pwdd"

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 (webLoginApi(username, userpwd)) {
            responseResult("成功", 200)
        } else {
            responseResult("失败", 400)
        }
    } else {
        TODO("用户名或密码不合格")
    }
}

private fun webLoginApi(name: String?, pwd: String?): Boolean {
    return name == USER_NAME && pwd == USER_PWD
}

简略写法

fun main() {
    // 第一种方式
    loginApi("name", "pwdd", { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    })
    
    // 第二种方式
    loginApi("name", "pwdd", responseResult = { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    })
    
    // 第三种方式,简略写法
    loginApi("name", "pwdd") { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    }
}

内联函数

inline
如果函数使用lambda做为参数,需要声明成内联,否则在调用时会生成多个对象来完成lambda的调用,引起性能损耗。
如果函数使用内联,相当于C++的#define 宏定义 宏替换,会把代码替换到调用处,没有任何新建函数、新建对象的损耗

fun main() {
    loginApi("name", "pwdd") { msg: String, code: Int ->
        println("msg: $msg, code: $code")
    }
}

const val USER_NAME = "name"
const val USER_PWD = "pwdd"

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 (webLoginApi(username, userpwd)) {
            responseResult("成功", 200)
        } else {
            responseResult("失败", 400)
        }
    } else {
        TODO("用户名或密码不合格")
    }
}

fun webLoginApi(name: String?, pwd: String?): Boolean {
    return name == USER_NAME && pwd == USER_PWD
}

函数引用

lambda属于函数类型的 对象,在使用函数作为参数时,需要把普通函数通过 :: 转为函数类型的对象

fun main() {
    login("name", "pwdd", ::printResult)
    // 或
    val obj = ::printResult
    login("name", "pwdd", obj)
}

const val USER_NAME = "name"
const val USER_PWD = "pwdd"

fun printResult(msg: String, code: Int) {
    println("结果:msg: $msg, code: $code")
}

inline fun login(username: String, pwd: String, responseResult: (String, Int) -> Unit) {
    if (username == USER_NAME && pwd == USER_PWD) {
        responseResult("成功", 200)
    } else {
        responseResult("失败", 400)
    }
}

使用函数做为返回类型

fun main() {
    val result = showMethod("info")
    println(result("张三", 30))
}

fun showMethod(info: String): (String, Int) -> String {
    println("show info: $info")

    return { name: String, age: Int ->
        "我是匿名函数:name: $name, age: $age"
    }
}

匿名函数与具名函数

fun main() {
    // 匿名函数
    showPersonInfo("张三", 30) {
        println("$it")
    }

    // 具名函数
    showPersonInfo("张三", 30, ::showResultImpl)
}

fun showResultImpl(info: String) {
    println(info)
}

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

三、Kotlin内置函数


Kotlin可空性特点

?

image.png


安全调用操作符

?

image.png


带let的安全调用

fun main() {
    var name: String? = null
    // name = "张三"

    val result = name?.let {
        // it == name 本身
        // 如果能执行到这里,it 一定不为 null
        if (it.isBlank()) {
            "default"
        } else {
            "[$it]"
        }
    }
    println(result)  // null
}

非空断言操作符

!!

image.png


空合并操作符

?:

image.png


异常处理与自定义异常

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

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

class CustomException: IllegalArgumentException("代码不严谨")

先决条件函数

checkNotNull()
requireNotNull()
require()

image.png


substring

image.png

split

fun main() {
    val text = "A,B,C,D"
    val list = text.split(",")
    // 直接输出
    println(list)
    // 解构,C++中也有解构
    val(v1, v2, v3, v4) = list
    println("v1:$v1, v2:$v2, v3:$v3, v4:$v4")
}

replace

fun main() {
    val sourcePwd = "ABCDEFGHI"
    val newPwd = sourcePwd.replace(Regex("[ACF]")) {
        when (it.value) {
            "A" -> "1"
            "C" -> "2"
            "F" -> "3"
            else -> it.value
        }
    }
    println(newPwd)
}

== 与 === 比较操作

== 值比较
=== 引用比较


数字类型的安全转换函数

toXxxOrNull()

fun main() {
    val number = "888.8".toIntOrNull()
    println(number ?: "转换错误")
}

Double 转 Int

fun main() {
    println(123.4567.toInt())       // 123, 只保留整数
    println(123.4567.roundToInt())  // 123, 四舍五入
    println(123.5567.toInt())       // 123
    println(123.5567.roundToInt())  // 124
    val r = "%.2f".format(123.4567)
    println(r)                      // 123.46, 四舍五入
}

apply 内置函数

1.匿名函数中自动生成this参数,代表对象本身
2.apply函数始终返回的是对象本身,一般用于链式调用

val file = File("文件路径")
file.setExecutable(true)
file.setReadable(true)
println(file.readLine())
// 等价于
val file = File("文件路径")
file.apply {
    // apply函数默认生成一个代表当前对象的this
    setExecutable(true)
}.apply {
    setReadable(true)
}.apply {
    println(readlnOrNull())
}

let 内置函数

1.匿名函数中自动生成it参数,代表对象本身
2.最后一行做为返回值

fun main() {
    val result = listOf(6, 4, 7, 9).let {
        // it == 对象本身
        // 最后一行做为返回值
        it.first() + it.first()
    }
    println(result)

    val result2 = getMethod(null)
    println(result2)
}

fun getMethod(value: String?): String {
    return value?.let {
        it
    } ?: "传值为null"
}

run 内置函数

1.匿名函数中自动生成this参数,代表对象本身,一般用于值类型发生改变的链式调用
2.最后一行做为返回值

fun main() {
    val str = "12345"
    // run 与 具名函数
    str
        .run(::isLengthOk)
        .run(::showText)
        .run(::mapText)
        .run(::println)

    // run 与 匿名函数
    str
        .run {
            length > 3
        }
        .run {
            if (this) "长度ok" else "长度不够"
        }
        .run {
            "[$this]"
        }
        .run {
            println(this)
        }
}

fun isLengthOk(str: String) = str.length > 3

fun showText(isLengthOK: Boolean) = if (isLengthOK) "长度ok" else "长度不够"

fun mapText(showText: String) = "[$showText]"

with 内置函数

1.匿名函数中自动生成this参数,代表对象本身,一般用于值类型发生改变的链式调用
2.最后一行做为返回值
与 run 函数基本相同,只是调用方式不同

fun main() {
    val str = "12345"

    // with 与 具名函数
    val r1 = with(str, ::isLengthOk)
    val r2 = with(r1, :: showText)
    val r3 = with(r2, ::mapText)
    with(r3, ::println)

    // with 与 匿名函数
    val r11 = with(str) {
        length > 3
    }
    val r22 = with(r11) {
        if (this) "长度ok" else "长度不够"
    }
    val r33 = with(r22) {
        "[$this]"
    }
    with(r33) {
        println(this)
    }
}

fun isLengthOk(str: String) = str.length > 3

fun showText(isLengthOK: Boolean) = if (isLengthOK) "长度ok" else "长度不够"

fun mapText(showText: String) = "[$showText]"

also 内置函数

1.匿名函数中自动生成it参数,代表对象本身
2.also函数始终返回的是对象本身,一般用于链式调用

fun main() {
    val file = File("文件路径")
    file
        .also {
            it.setExecutable(true)
            it.setReadable(true)
        }
        .also {
            println(it.readLines())
        }
}

takeIf 内置函数

如果所传递函数对象中的条件满足,就返回自身
一般情况下,takeIf + 空合并操作符 一起使用

fun main() {
    val result = checkPermission("root", "12345")
    println(result)
}

fun checkPermission(username: String, userpwd: String): String {
    // 一般情况下,takeIf + 空合并操作符 一起使用
    return username.takeIf { checkInternal(username, userpwd) } ?: "普通用户"
}

private fun checkInternal(username: String, userpwd: String): Boolean {
    return username == "root" && userpwd == "12345"
}

takeUnless 内置函数

如果所传递函数对象中的条件不满足,就返回自身
与 takeIf 功能相反
takeUnless+对象中的属性判空一般一起使用,可以验证对象的属性有没有设置值

class Manager {
    private var info: String? = null

    fun getInfo() = info

    fun setInfo(info: String) {
        this.info = info
    }
}

fun main() {
    val manager = Manager()

    // takeUnless+对象中的属性判空一般一起使用,可以验证对象的属性有没有设置值
    val r = manager.getInfo().takeUnless { it.isNullOrBlank() } ?: "未初始化"
    println(r)
}

四、集合与对象


List

尽量使用 getOrElse 或 getOrNull 获取数组中元素

fun main() {
    val list = listOf("A", "B", "C")

    println(list[0])
    println(list.getOrElse(4) { "越界" })
    println(list.getOrNull(4) ?: "越界")
}

可变 List

fun main() {
    val list = mutableListOf("A", "B", "C")
    list.add("D")
    list.remove("D")
    
    val list2 = listOf(1, 2, 3)
    // list2.add  // 没有这个函数
    // 不可变 List 转 可变 List
    val list3 = list2.toMutableList()
    
    // 可变 List 转 不可变 List
    val list4 = list.toList()
}

mutator函数、removeIf

+= -=,靠运算符重载实现
removeIf 有条件的移除List中元素,会自动遍历元素

fun main() {
    val list = mutableListOf("Zhangsan", "Lisi", "Zhangsi", "Wangwu")
    // mutator: += -=
    list += "赵六"
    list -= "Zhangsi"
    println(list)

    // removeIf
    list.removeIf { it.contains("Zhang") }
    println(list)
}

List 遍历

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

    // 第一种
    for (item in list) {
        println(item)
    }

    // 第二种
    list.forEach {
        println(it)
    }

    // 第三种,带位置索引
    list.forEachIndexed { index, item ->
        println("index: $index, item: $item")
    }
}

解构语法过滤元素

fun main() {
    val list = listOf("Zhangsan", "Lisi", "Wangwu")

    // 解构,元素值不可变
    val(value1, value2, value3) = list
    println("value1: $value1, value2: $value2, value3: $value3")

    // 解构,元素值可变
    var(v1, v2, v3) = list
    v1 = "ok"
    println("v1: $v1, v2: $v2, v1: $v3")

    // 解构时过滤元素,可以节省一点性能
    val(_, n2, n3) = list
    println("n2: $n2, n3: $n3")  // 没有第一个
}

Set

不会出现重复元素
尽量使用 elementAtOrElse 或 elementAtOrNull 获取元素

fun main() {
    val set = setOf("A", "B", "C", "A")

    println(set.elementAt(0))
    // println(set.elementAt(3)) // 崩溃
    println(set.elementAtOrElse(0) { "越界" })
    println(set.elementAtOrElse(3) { "越界" })
    println(set.elementAtOrNull(1) ?: "越界")
    println(set.elementAtOrNull(3) ?: "越界")
}

可变 Set

fun main() {
    val set = mutableSetOf("A", "B", "C", "A")
    set += "D"
    set -= "D"
    set.add("D")
    set.remove("D")
    set.removeIf { 
        it == "D"
    }
}

集合转换与快捷函数

fun main() {
    val list = listOf("A", "A", "B", "C")

    // List 转 Set 去重
    val set = list.toSet()
    println(set)

    // List 转 Set 转 List 去重
    val list2 = list.toSet().toList()
    println(list2)

    // 快捷函数 distinct 去重
    val list3 = list.distinct()  // 内部:转 可变Set 转 List
    println(list3)
}

数组

尽量使用elementAtOrElse 或 elementAtOrNull 获取元素

        Kotlin语言中的各种数组类型,虽然是引用类型,背后可以编译成Java基本数据类型
        IntArray        intArrayOf
        DoubleArray     doubleArrayOf
        LongArray       longArrayOf
        ShortArray      shortArrayOf
        ByteArray       byteArrayOf
        FloatArray      floatArrayOf
        BooleanArray    booleanArrayOf
        Array           arrayOf             对象数组
fun main() {
    val intArray = intArrayOf(1, 2, 3)

    // 取元素
    intArray.elementAt(0)
    // intArray.elementAt(3)  // 崩溃

    intArray.elementAtOrElse(3) { -1 }
    intArray.elementAtOrNull(3) ?: "越界"

    // List 转 数组
    val charArray = listOf('A', 'B').toCharArray()

    // 对象数组
    val objArray = arrayOf(File("1"), File("2"))
}

Map

fun main() {
    val map1 = mapOf("A" to(1.0), "B" to 2.0)
    val map2 = mapOf(Pair("A", 1.0), Pair("B", 2.0))
}

Map 中值的获取

尽量使用 getOrDefault 或 getOrElse

fun main() {
    val map = mapOf("A" to (1.0), "B" to 2.0)

    // 方式一 [],等价于 get,获取不到返回 null
    println(map["A"])
    println(map.get("A"))
    println(map["C"])

    // 方式二 getOrDefault
    println(map.getOrDefault("A", -1.0))
    println(map.getOrDefault("C", -1.0))

    // 方式二 getOrDefault
    println(map.getOrElse("A") { "找不到" })
    println(map.getOrElse("C") { "找不到" })

    // 方式三 getValue,获取不到会崩溃
    println(map.getValue("A"))
    println(map.getValue("C"))
}

Map 的遍历

fun main() {
    val map = mapOf("A" to (1.0), "B" to 2.0)

    // 方式一
    map.forEach {
        println("key: ${it.key}, value: ${it.value}")
    }

    // 第二种
    map.forEach { key, value ->
        println("key: $key, value: $value")
    }

    // 第三种
    map.forEach { (key, value) ->
        println("key: $key, value: $value")
    }

    // 第四种
    for (item in map) {
        println("key: ${item.key}, value: ${item.value}")
    }
}

可变 Map

fun main() {
    val map = mutableMapOf("A" to (1.0), "B" to 2.0)

    // 操作:+= -= [] put
    map += "C" to 3.0
    map -= "C"
    map["C"] = 3.0
    map.put("C", 3.0) // 等价于 []

    // getOrPut 没有的话就添加进去
    map.getOrPut("D") { 4.0 }
    println(map["D"])

    // getOrPut 有的话就取出来
    map.getOrPut("A") { 4.0 }

    // getOrDefault 没有就取传递的默认值
    val r = map.getOrDefault("F", 6.0)
    println(r)
}

定义类、field关键词

对于已经赋初始值的属性,field 代表当前属性的值

class KtBaseClass {
    var name = "zhangsan"
    // 自动生成隐式代码,写不写都有
        get() = field
        set(value) {
            field = value
        }

    var info = "is ok"
    // 重写隐式代码,扩展功能
        get() = field.capitalize()
        set(value) {
            field = "**${value}**"
        }
}

类的计算属性 与 防范竞态条件

计算属性:使用get函数覆盖field的写法
防范竞态条件:当被调用的属性可能为null时,需要使用防空指针的写法编写代码,这种写法称为防范竞态条件

class KtBaseClass {
    val number: Int
        // 计算属性,get函数覆盖了field,在对应的java代码中,也不会生成对应名称的属性
        get() = (1..1000).shuffled().first()

    val info: String? = null
    // 防范竞态条件,当被调用的成员可能为null时,就必须使用防范竞态条件
    fun getShowInfo(): String {
        // 这种写法就是防范竞态条件,这种写法会大量使用
        return info?.let {
            if (it.isBlank()) {
                "info 是 空值"
            } else {
                "info 结果:$it"
            }
        } ?: "info 是 null"
    }
}

类的主构造函数

// 主构造函数:隐式自动生成,不需要写
class KtBaseClass() {

}

// 主构造函数:传入的值命名要按照 _xxx 的方式,传入的值不能直接使用,需要接收后才能使用
class KtBaseClass(_name: String, _sex: Char, _age: Int) {

    var name = _name
        get() = field  // get不允许private
        private set(value) {
            field = value
        }

    val sex = _sex
        get() = field
        // set(value) {}  // 声明为val不可修改,没有set

    var age = _age
        get() = if (field < 0) 0 else field
        // 或
//        get() {
//            return if (field < 0) {
//                0
//            } else {
//                field
//            }
//        }

    fun show() {
        // println(_name) // 不允许直接使用
    }
}

fun main() {
    val p = KtBaseClass("zhangsan", 'm', -1)
    println(p.age)
}

在类的主构造函数中定义属性

// 在主构造函数中直接定义属性
class KtBaseClass(var name: String, val sex: Char, var age: Int) {

    fun show() {
        println(name)
    }
}

类的次构造函数

class KtBaseClass(var name: String) {  // 主构造

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


fun main() {
    val p = KtBaseClass("zhangsan", 'm')
    println(p.name)
}

构造函数中参数的默认值

class KtBaseClass(var name: String = "zhangsan") {  // 主构造

    constructor(name: String = "zhangsan", sex: Char = 'm') : this(name) {  // 次构造函数必须调用主构造函数
        println("次构造函数:name: $name, sex: $sex")
    }
}


fun main() {
    val p = KtBaseClass("zhangsan", 'm')  // 调用次构造函数
    println(p.name)

    val p2 = KtBaseClass()  // 优先调用主构造函数
}

类的初始化块

class KtBaseClass(_name: String) {  // 主构造

    // 相当于Java的 {} 构造代码块,在主构造函数执行时执行
    // 可以使用构造函数传入的变量
    init {
        println("主构造函数调用:name: $_name")

        // 可以在这里判断传入值的合法性,验证不通过会抛出异常
        require(_name.isNotBlank()) {
            "name不能是空值"
        }
    }

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

fun main() {
    KtBaseClass("zhangsan", 'm')  // 调用次构造函数
}

构造初始化顺序

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

    // 第二步:生成 val name。与init代码块平级,写在init代码块前所以先执行
    val name = _name

    init {
        val nameValue = _name  // 第三步:生成 val nameValue
        println("init代码块调用:name: $_name")
    }


    constructor(name: String, sex: Char, age: Int) : this(name, sex) {  // 次构造函数必须调用主构造函数
        // 第五步:执行次构造函数
        println("次构造函数:name: $name, sex: $sex, age: $age")
    }

    // 第四步:生成 val info
    val info = "test info"
}

fun main() {
    KtBaseClass("zhangsan", 'm')  // 调用次构造函数
}

延迟初始化 lazyinit

使用 lazyinit 的属性前需要手动初始化

class KtClass {
    lateinit var info: String

    fun loadInfo() {
        info = "初始化成功"
    }

    fun showInfo() {
        // if (info == null) {} // 不能这样用,在info初始化前任何使用都会崩溃
        if (::info.isInitialized) {
            println("info: $info")
        } else {
            println("info还没初始化")
        }
    }
}

fun main() {
    val p = KtClass()
    // println(p.info)  // lazyinit的属性在初始化前使用会导致崩溃
    
//    p.loadInfo()
//    println(p.info)

    p.showInfo()
}

惰性初始化 by lazy

属性在使用时自动初始化

class KtClass {
    // 普通方式(饿汉式)
    val info1 = readFromDB1()

    val info2 by lazy { readFromDB2() }

    fun readFromDB1(): String {
        println("1开始读取数据")
        println("1读取数据中...")
        println("1读取数据中...")
        println("1读取数据中...")
        println("1读取数据中...")
        println("1结束读取数据")
        return "info1 load success"
    }

    fun readFromDB2(): String {
        println("2开始读取数据")
        println("2读取数据中...")
        println("2读取数据中...")
        println("2读取数据中...")
        println("2读取数据中...")
        println("2结束读取数据")
        return "info2 load success"
    }
}

fun main() {
    val p = KtClass()

    Thread.sleep(5000L)

    println("即将开始使用info1")
    println("读取到的数据:info: ${p.info1}")
    println()
    println("即将开始使用info2")
    println("读取到的数据:info: ${p.info2}")
}

// 输出
1开始读取数据
1读取数据中...
1读取数据中...
1读取数据中...
1读取数据中...
1结束读取数据
即将开始使用info1
读取到的数据:info: info1 load success

即将开始使用info2
2开始读取数据
2读取数据中...
2读取数据中...
2读取数据中...
2读取数据中...
2结束读取数据
读取到的数据:info: info2 load success

初始化陷阱

注意属性与init代码块定义代码顺序

class KtClass2 {
    
    init {
        // 与 var name 生成处于同一优先级,执行顺序与定义顺序有关,所以不能这样用,必须先定义 val number
        number = number.times(9)
    }

    var number = 9
}

初始化陷阱2

class KtClass3 {

    var info: String

    init {
        getInfoMethod()  // info 还没初始化就调用了
        info = "test info"  // 需要把这行放在上一行上面 
    }

    fun getInfoMethod() {
        println("info: ${info[0]}")
    }
}

fun main() {
    KtClass3()  // 会崩溃
}

初始化陷阱3

class KtClass5(_info: String) {

    var content: String = getInfoContent()

    var info = _info  // 需要把这行放到最前面

    fun getInfoContent() = info
}

fun main() {
    KtClass5("test").content.length  // 会崩溃
}

五、Kotlin语言特点


类的继承与重载的 open 关键词

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

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

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

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

    private fun showName() = "子类的姓名是$subName"

    override fun myPrint() = println(showName())
}

fun main() {
    val person: Person = Studend("zhangsan")
    person.myPrint()
}

类型转换

is
as

open class Person(private val name: String) {

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

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

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

    private fun showName2() = "子类的姓名是$subName"

    override fun myPrint() = println(showName2())
}

fun main() {
    val person: Person = Student("zhangsan")
    person.myPrint()

    println(person is Student)  // true
    println(person is Person) // true

    // is + as
    if (person is Student) {
        (person as Student).myPrint()
    }

    if (person is Person) {
        println((person as Person).showName())
    }
}

智能类型转换

在语句中使用 as 对对象obj进行类型转换后,后就面的语句会自动判断obj的类型为转换后的类型

open class Person(private val name: String) {

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

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

    fun methodPerson() = println("父类的方法")
}

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

    private fun showName2() = "子类的姓名是$subName"

    override fun myPrint() = println(showName2())

    fun methodStudent() = println("子类的方法")
}

fun main() {
    val person: Person = Student("zhangsan")

    // 智能类型转换:这里调用了 as 转换,下面的语句中的person会自动判断为Student类型
    (person as Student).methodStudent()
    person.methodStudent()  // 不需要再as进行转换
}

超类 Any

在Kotlin中所有的类都隐式继承了 Any,不需要写
Any 类在Kotlin的设计中,只提供标准,看不到实现,实现在各个平台处理好了
相当于Java的Object,但是比Object实现的更高级

class Obj1: Any()

fun main() {
    println(Obj1().toString())
}

对象声明

object 类名即是类名,又是类的单例,只有一个创建,是典型的单例

// object KtObject 即是类的实例,又是类名
// 只有一个创建这是典型的单例
object KtObject {

    init {
        println("KtObject init")
    }

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

fun main() {
    println(KtObject) // 三个打印一致
    println(KtObject)
    println(KtObject)

    KtObject.show()
}

对象表达式

匿名对象表达式:objct: 类名()
具名对象是常见的类声明及对象初始化
对于Java接口,有两种实现方式:object: 对象表达式 和 简洁版
对于Kotlin接口,只有一种实现方式:object: 对象表达式

interface RunnableKt {
    fun run()
}

open class KtBaseClass {

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

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

fun main() {
    // 匿名对象 表达式
    val p = object: KtBaseClass() {
        override fun add(info: String) {
            println("匿名对象add: $info")
        }

        override fun del(info: String) {
            println("匿名对象del: $info")
        }
    }
    p.add("zhangsan")
    p.del("zhangsan")

    // 具名方式实现
    val p2 = KtBaseClassImpl()
    p2.add("zhangsan")
    p2.del("zhangsan")

    // 对Java的接口,用Kotlin对象表达式实现
    val p3 = object: Runnable {
        override fun run() {
            println("Runnable run ...")
        }
    }
    p3.run()

    // 对Java接口,用Java最简介方式实现
    val p4 = Runnable {
        fun run() {
            println("Runnable run2 ...")
        }
    }
    p4.run()

    // 对Kotlin的接口,用Kotlin对象表达式实现
    object: RunnableKt {
        override fun run() {
            println("RunnableKt run ...")
        }
    }.run()

    // 对Kotlin接口,用Java最简介方式实现,不能这样用
//    RunnableKt {
//
//    }
}

class KtBaseClassImpl: KtBaseClass() {
    override fun add(info: String) {
        println("具名对象add: $info")
    }

    override fun del(info: String) {
        println("具名对象del: $info")
    }
}

伴生对象

由来:Kotlin中没有Java的static,伴生对象类似于Java的static
无论类的对象构建多少次,其中的伴生对象只初始化一次
无论伴生对象中的成员被调用多少次,伴生对象只初始化一次

class KtCompanion {

    // 伴生对象
    companion object {
        val info = "test info"
        fun show() = println("info: $info")
    }
}

fun main() {
    println(KtCompanion.Companion.info)

    KtCompanion.Companion.show()

    KtCompanion()
    KtCompanion()
}

内部类

inner 修饰
内部类 能访问 外部类(加inner修饰),外部类 能访问 内部类
嵌套类 不能访问 外部类,外部类 能访问 嵌套类

// 内部类
class Body(_info: String) {

    val bodyInfo = _info

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

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

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

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

// 嵌套类
class Outer {

    val info = "ok"

    fun show() {
        Nested().output()  // 外部类 能访问 嵌套类
    }

    class Nested {

//        fun output() = println("嵌套类: $info")  // 嵌套类 不能访问 外部类
        fun output() = println("嵌套类")
    }
}

fun main() {
    // 内部类
    Body("ok").Heart().run()

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

数据类

data 修饰符

// 普通类
class ResponseResultBean1(var code: Int, var msg: String, var data: String)

// 数据类,一般用于 Bean 类,默认提供了 get set 构造函数 解构操作 copy hashCode toString equals
data class ResponseResultBean2(var code: Int, var msg: String, var data: String)

fun main() {
    println(ResponseResultBean1(200, "ok", "data"))

    println(ResponseResultBean2(200, "ok", "data"))  // 打印属性的值

    println(
        ResponseResultBean1(200, "ok", "data") == ResponseResultBean1(200, "ok", "data")
    )  // false

    println(
        ResponseResultBean2(200, "ok", "data") == ResponseResultBean2(200, "ok", "data")
    )  // true
}

数据类默认的 copy equals hashCode toString 函数

数据类默认的 copy equals hashCode toString 函数只管主构造函数中的属性,不管次构造函数中的属性,使用以上函数时需要注意

data class DataClass(var name: String , var age: Int) {

    var coreInfo = ""

    constructor(name: String): this(name, 20) {
        coreInfo = "核心信息"
    }

    // 需要重写才有次构造函数中初始化的属性值
    override fun toString(): String {
        return "toString name: $name, age: $age, coreInfo: $coreInfo"
    }
}

fun main() {
    val p1 = DataClass("zhangsan")
    println(p1)

    val p2 = p1.copy("lisi", 23)
    println(p2)
}

解构声明

解构必须是按照顺序声明的,且从 component1 开始

class Student(var name: String, var age: Int, var sex: Char) {

    operator fun component1() = name
    operator fun component2() = age
    operator fun component3() = sex
}

fun main() {
    val(name, age, sex) = Student("zhangsan", 20, 'm')
    println("name: $name, age: $age, sex: $sex")

    val(name2, age2, _) = Student("zhangsan", 20, 'm')
    println("name: $name2, age: $age2")
}

运算符重载

class AddClass(var number1: Int , var number2: Int) {
    operator fun plus(p: AddClass): Int {
        return number1 + p.number1 + number2 + p.number2
    }
}

fun main() {
    val p1 = AddClass(1, 2)
    val p2 = AddClass(3, 4)
    println(p1 + p2)
}

枚举

enum class
枚举的值等价于枚举本身

enum class Week {
    星期一,
    星期二,
    星期三,
    星期四,
    星期五,
    星期六,
    星期日;
}

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

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

枚举类定义函数

data class LimbsInfo(var name: String, var length: Int) {
    fun show() {
        println("${name}的长度是$length")
    }
}

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

    LEFT_FOOT(LimbsInfo("左脚", 100)),
    RIGHT_FOOT(LimbsInfo("右脚", 100));

    fun show() = "四肢信息:${limbsInfo.name},长度:${limbsInfo.length}"

    // 更新
    fun update(limbsInfo: LimbsInfo) {
        this.limbsInfo.name = limbsInfo.name
        this.limbsInfo.length = limbsInfo.length
    }
}

fun main() {
    println(Limbs.LEFT_HAND.show())

    // 更新
    Limbs.LEFT_HAND.update(LimbsInfo("左手", 89))
}

代数数据类型

when 判断枚举值

enum class Exams {
    Fraction1,
    Fraction2,
    Fraction3,
    Fraction4
}

class Teacher(private val exam: Exams) {
    fun show() =
        when (exam) {
            Exams.Fraction1 -> "不及格"
            Exams.Fraction2 -> "及格"
            Exams.Fraction3 -> "优良"
            Exams.Fraction4 -> "优秀"
            // else -> 上面已经包含全部值,可省略
        }
}

fun main() {
    println(Teacher(Exams.Fraction4).show())
}

密封类

scaled
成员必须有类型,并且继承本类

sealed class Exams {
     object Fraction1: Exams()
     object Fraction2: Exams()
     object Fraction3: Exams()
     class Fraction4(val name: String): Exams();
}

class Teacher(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).name}"
        }
}

fun main() {
    println(Teacher(Exams.Fraction3).show())
    println(Teacher(Exams.Fraction4("zhangsan")).show())
}

数据类使用条件

1. 服务器返回的响应数据 Java Bean 基本可以使用数据类
2. 数据类必须有至少一个参数的主构造函数
3. 数据类必须有参数
4. 数据类不能使用 open abstract inner scaled 等修饰
5. 需要 比较、copy、toString、解构 等丰富功能时,可使用数据类


六、

接口的定义

interface

interface IUSB {
    var versionInfo: String
    var deviceInsertInfo: String

    fun insert(): String
}

class Mouse(override var versionInfo: String = "USB 3.0", override var deviceInsertInfo: String = "鼠标插入USB接口") : IUSB {
    override fun insert(): String = "Mouse $versionInfo: $deviceInsertInfo"
}

class Keyboard: IUSB {
    override var versionInfo: String = "USB 3.1"
        get() = field
        set(value) {
            field = value
        }

    override var deviceInsertInfo: String = "键盘插入USB接口"
        get() {
            println("你get了$field")
            return field
        }
        set(value) {
            field = value
            println("你set了$value")
        }

    override fun insert(): String = "Keyboard $versionInfo: $deviceInsertInfo"
}

接口的默认实现

虽然接口成员可以通过 get set 给其赋默认值,但是不建议这样做,因为接口本来就是用来定义标准的

interface IUSB {
    // 接口成员不论是val还是var,都不能给其赋值(但有其它方法:get)
    // 任何类、接口的val成员是只读的,不能动态改变其值(但是有其它方法:set)
    val versionInfo: String
        get() = (1..100).shuffled().first().toString()
        // set(value) val 不能有set

    val deviceInsertInfo: String
        get() = "接入设备"
        // set(value) val 不能有set

    fun insert(): String
}

class Mouse() : IUSB {

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

    override val deviceInsertInfo: String
        get() = super.deviceInsertInfo
    
    override fun insert(): String = "Mouse $versionInfo: $deviceInsertInfo"
}

抽象类

abstract class BaseActivity {

    fun onCreate() {
        setContentView(getLayoutId())
        initView()
        initData()
    }

    private fun setContentView(layoutId: Int) {
        println("加载布局:$layoutId")
    }

    abstract fun getLayoutId(): Int
    abstract fun initView()
    abstract fun initData()
}

class MainActivity: BaseActivity() {
    override fun getLayoutId(): Int = 123

    override fun initView() {
        println("init view")
    }

    override fun initData() {
        println("init data")
    }

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

fun main() {
    MainActivity().show()
}

泛型类

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

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

fun main() {
    val student = Student2("zhangsan", 22, 'm')
    KtBaseTClass(student).show()
}

泛型函数

与 let apply also run 等结合使用

class KtClassGetter<T>(private val isOk: Boolean, private val obj: T) {
    fun getClass() {
        obj.takeIf { isOk }
    }
}

泛型类型转换

fun <I, O> map(inputType: I, isMap: Boolean = true, mapAction: (I) -> O) =
    if (isMap) mapAction(inputType) else null

fun main() {
    val r = map(123) {
        it.toString()
    }
    println(r)
}

泛型类型约束

<T: xxx> 代表只接收 xxx 及其 子类

class KtClassTranslator<T: Student>(private val inputType: T, private val isR: Boolean = true) {
    fun getObj() = inputType.takeIf { isR }
}

vararg关键词(动态参数)

class KtVarargClass<T>(vararg objects: T, var isMap: Boolean) {

    // out: T 只能被读取
    private val objectArray: Array<out T> = objects

    fun showObj(index: Int) = objectArray[index].takeIf { isMap }

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

fun main() {
    // 由于传递的动态参数中,包含多种类型,所以泛型真正的类型是 KtVarargClass<{Comparable<*> & java.io.Serializable}>
    // 但是不允许这样写,所以使用 Any? 代替
    val p: KtVarargClass<Any?> = KtVarargClass("zhangsan", 1234, 5678.9, null, false, isMap = true)

    println(p.showObj(0))
    println(p.showObj(3))

    // map
    // mapAction中声明T为可空的(?),所以it也是可空的(?)
    val r = p.mapObj(1) {
        "转化为String: $it"
    }
    println(r)

    val p2 = KtVarargClass("zhangsan", "lisi", "wangwu", isMap = true)
    println(p2.showObj(0))

    // p2传递的动态参数中,只包含String类型,所以it的类型为String?
    val r2 = p2.mapObj(1) {
        it?.length
    }
    println(r2)
}


[ ]操作符

class KtGetOperator<T>(vararg val objects: T, var isR: Boolean = true) {

    // []运算符重载
    operator fun get(index: Int): T? {
        return objects[index].takeIf { isR }
    }
}

fun main() {
    val p = KtGetOperator("zhangsan", "lisi", null)
    // 只要有一个元素为null,那么所有的元素都是 String?
    val r: String? = p.get(1)
    println(p.get(0))
    println(p.get(2))
}

out 协变

在父类泛型声明处,可以接受子类

// 生产者 out T 协变,T只能被读取,不能修改
interface Producer<out T> {

    // 编译错误:T不能被修改
    // fun consumer(item: T)

    fun product(): T
}

// 消费者 in T 逆变,T不能被读取,只能修改
interface Consumer<in T> {

    // 编译错误:T不能被修改
    fun consumer(item: T)

    // 编译错误:T不能被读取
    // fun product(): T
}

// 生产者与消费者 T 默认情况下是不变,T能被修改,也能读取
interface ProducerAndConsumer<T> {

    fun consumer(item: T)

    fun product(): T
}

open class Animal
open class Human: Animal()
open class Man: Human()
open class Woman: Human()

class ProducerClass1: Producer<Animal> {
    override fun product(): Animal {
        println("生产者 Animal")
        return Animal()
    }
}

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

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

class ProducerClass4: Producer<Woman> {
    override fun product(): Woman {
        println("生产者 Woman")
        return Woman()
    }
}

fun main() {
    val p1: Producer<Animal> = ProducerClass1()
    // ProducerClass2声明的泛型为Human,p2声明为Animal不报错,是因为泛型声明为 out,如果非 out 会报错
    // out 相当于java中的 ? extends T,
    // 例如:List<? extends CharSequence> list = new ArrayList<String>()
    val p2: Producer<Animal> = ProducerClass2()
    val p3: Producer<Animal> = ProducerClass3()
    val p4: Producer<Animal> = ProducerClass4()
}

in 和 out

使用场景:
in 逆变:只能修改,不能读取
out 协变:只能读取,不能修改

class SetClass<in T> {

    fun set1(item: T) {

    }
}

class GetClass<out T>(_item: T) {

    val item = _item
    
    fun get1(): T {
        return item
    }
}

refield 关键词

class ObjectClass1(val name:String, val age: Int, val sex: Char)
class ObjectClass2(val name:String, val age: Int, val sex: Char)
class ObjectClass3(val name:String, val age: Int, val sex: Char)

class RandomClass {

    inline fun <reified T> randomOrDefault(defaultAction: () -> T): T {
        val objList = listOf(
            ObjectClass1("zhangsan", 22, 'm'),
            ObjectClass2("lisi", 23, 'm'),
            ObjectClass3("wangwu", 24, 'm'),
        )
        val randomObj = objList.shuffled().first()

        println("产生的随机对象:$randomObj")

        // 如果不声明 reified T,it as T 会报错
        // 注意 as T?,当takeIf条件不成立时,null as T 会导致崩溃,所以要 T?
        return randomObj.takeIf { it is T } as T? ?: defaultAction()
    }
}

fun main() {
    val result = RandomClass().randomOrDefault<ObjectClass1> {
        println("使用默认值")
        ObjectClass1("zhangsan", 22, 'm')
    }

    println("结果:$result")
}

定义扩展函数

类名.扩展函数名()

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

// 扩展函数,会自动生成this引用
fun KtExtensionBaseClass.show() = println("显示信息:name:$name, age:$age, sex:$sex")

// 其它框架中的类也可以添加扩展函数
fun String.addAction(count: Int) = this + "@".repeat(count)

fun main() {
    KtExtensionBaseClass("zhangsan", 22, 'm').show()
    println("lisi".addAction(3))
}

在超类上定义扩展函数

1. 自己定义的扩展函数不能重复
2. Kotlin内置的扩展函数,我们可以重新定义进行覆盖

fun Any.showContent() = println("内容是:$this")

fun Any.showContent2(): Any {
    println("内容2是:$this")
    return this
}

fun File.readLine() {
    println("覆盖系统的扩展函数")
}

data class ResponseResult(val msg: String, val code: Int)

fun main() {
    ResponseResult("success", 200).showContent()

    "zhangsan".showContent2().showContent()

    File("").readLines()
}

泛型扩展函数

fun <T> T.showContentInfo() = println("${if (this is String) "字符串长度是:$length" else "不是字符串,内容是:$this"}")

fun commonFun() {}

fun main() {
    "test".showContentInfo()
    val p = 123
    p.showContentInfo()
    
    commonFun().showContentInfo()
}

标准函数与泛型扩展函数

private inline fun <I, O> I.mLet(lambda: (I) -> O) = lambda(this)

fun main() {
    val r = "test1".mLet {
        it.length
    }
    println(r)
}

val 类名.属性名
get() = "xxx"

扩展属性

val String.myInfo
    get() = "zhangsan"

fun main() {
    println("".myInfo)
}

可空类型的扩展函数

类名?.扩展函数名()

fun String?.outputValue(defaultValue: String) = println(this ?: defaultValue)

fun main() {
    val nullValue: String? = null
    nullValue.outputValue("默认值1")

    val value = "zhangsan"
    value.outputValue("默认值2")
}

infix 关键词

infix:中缀表达式,可以简化代码
1. 需要与扩展函数一起使用
2. 需要在函数的参数中传递一个参数

private infix fun <C1, C2> C1.gogo(c2: C2) {
    println("中缀表达式:第一个参数:$this")
    println("中缀表达式:第二个参数:$c2")
}

fun main() {
    // Kotlin自带,
    // 例如:public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
    mapOf("零".to(0))
    mapOf("一" to 1)
    mapOf("二" to 2)

    // 自定义
    "张三".gogo("李四")
    "wangwu" gogo 1
}

定义扩展文件

在实际中比较有用,可以把很多扩展操作定义在一个文件中,使用的时候引入,方便管理

KtExtension.kt 中定义

package com.demo.lib.ext

fun <E> Iterable<E>.randomItemValue() = this.shuffled().first()

MyClass.kt 中使用

import com.demo.lib.ext.randomItemValue

fun main() {
    val p = listOf("zhangsan", "lisi", "wangwu").randomItemValue()
    println(p)
}

重命名扩展

import xxx.xxx.xxx as xxx

import com.demo.lib.ext.randomItemValue as f

fun main() {
    val p = listOf("zhangsan", "lisi", "wangwu").f()
    println(p)
}

apply 函数详解

// INPUT.myApply,泛型扩展函数: 让所有的类型,都可以 xxx.myApply 调用
// INPUT.():让匿名函数持有 this
private inline fun <INPUT> INPUT.myApply(lambda: INPUT.() -> Unit): INPUT {
    lambda()
    return this
}

fun main() {
    File("xxx")
        .myApply {
            setReadable(true)
        }
        .myApply {
            setWritable(true)
        }
}

DSL (Domain Specified Language),领域专用语言

// apply5函数,就是DSL编程范式,定义输入输出等规则。
// 1.定义lambda规则标准,输入 必须是Context类,才能调用apply5函数
// 2.定义lambda规则标准,输出 始终返回Context本身
inline fun Context.apply5(lambda: Context.(String) -> Unit): Context {
    lambda(info)
    return this
}

inline fun File.applyFile(action: (String, String?) -> Unit): File {
    action(name, readLines()[0])
    return this
}

fun main() {
    val context = Context().apply5 {
        // 同时持有this和it
        toast("success")
        // it == String
        toast(it)
        toast(name)
    }
    println(context)

    File("xxx")
        .applyFile { fileName, content ->
            println("文件名:$fileName, 内容:$content")
        }
        .applyFile { fileName, content ->
            
        }
}

变换函数-map

map:生成一个新的集合,新集合中每个元素根据lambda表达式进行转换

fun main() {
    val list = listOf("zhansan", "lisi", "wangwu")
    list
        .map {
            "姓名:$it"
        }
        .map {
            println("$it")
        }
}

变换函数-flatMap

flatMap:生成一个新的集合,新集合中每个元素都是集合,新集合中每个元素根据lambda表达式进行转换生成

fun main() {
    val list = listOf("zhansan", "lisi", "wangwu")
    // flatMap返回的是 List<List<String>>,但最终会进行平铺返回 List<String>
    val newList: List<String> = list.flatMap {
        listOf("$it 在学习Java", "$it 在学习Kotlin")
    }
    println(newList)
}
// [zhansan 在学习Java, zhansan 在学习Kotlin, lisi 在学习Java, lisi 在学习Kotlin, wangwu 在学习Java, wangwu 在学习Kotlin]

过滤函数-filter

filter:生成一个新的集合,新集合中每个元素符合给定的规则

fun main() {
    val list = listOf(
        listOf("zhangsan", "lisi"),
        listOf("wangwu", "zhaoliu")
    )

    list.map {
        it -> it.filter {
            it.contains('z')
        }
    }.map {
        println(it)
        // [zhangsan]
        // [zhaoliu]
    }

    list.flatMap {
        it -> it.filter {
            it.contains('z')
        }
    }.map {
        println(it)
        // zhangsan
        // zhaoliu
    }
}

合并函数-zip

zip:根据两个集合中的元素生成一个新的集合,与原集合index一致,并且新集合元素数量为原两个集合数量最小值

fun main() {
    val nameList = listOf("zhangsan", "lisi", "wangwu")
    val ageList = listOf(22, 23)
    val newList = nameList.zip(ageList)
    println(newList)
    // [(zhangsan, 22), (lisi, 23)]

    newList.forEach {
        println("nane: ${it.first}, age: ${it.second}")
    }
    // nane: zhangsan, age: 22
    // nane: lisi, age: 23
}

函数式编程

简化代码,例如 zip 函数的 Java 实现远比 Kotlin 实现所用代码多很多

Kotlin与Java互操作性和可空性

Kotlin调用Java代码时,Java给Kotlin的值,都是类似于 String!,
所以kotlin在接受Java给的值时,直接把类型声明为可空?,避免代码出错

fun main() {
    // 错误
    println(JavaClass().info1.length)
    println(JavaClass().info2.length)  // 崩溃

    // 错误
    val info1 = JavaClass().info1
    val info2 = JavaClass().info2
    println(info1.length)
    println(info2.length)  // 崩溃

    // 正确
    // Kotlin调用Java代码时,Java给Kotlin的值,都是类似于 String!
    val info11 = JavaClass().info1
    val info21 = JavaClass().info2
    println(info11?.length)
    println(info21?.length)

    // 正确,推荐
    // Kotlin调用Java代码时,Java给Kotlin的值,都是类似于 String!
    // 所以kotlin在接受Java给的值时,直接把类型声明为可空?,避免代码出错
    val info12: String? = JavaClass().info1
    val info22: String? = JavaClass().info2
    println(info12?.length)
    println(info22?.length)
}

单例模式

1. 饿汉式

object SigletonDemo

2. 懒汉式

class SingletonDemo private constructor() {

    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }

        fun getInstanceAction(): SingletonDemo = instance !!
    }

    fun show() = println("show")
}

fun main() {
    SingletonDemo.getInstanceAction().show()
}

3. 懒汉式+线程安全:@Synchronized

class SingletonDemo private constructor() {

    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }

        @Synchronized
        fun getInstanceAction(): SingletonDemo = instance !!
    }

    fun show() = println("show")
}

fun main() {
    SingletonDemo.getInstanceAction().show()
}

4. 懒汉式+双重校验安全:by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { ... }

class SingletonDemo private constructor() {

    companion object {
        val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { SingletonDemo() }
    }

    fun show() = println("show")
}

fun main() {
    SingletonDemo.instance.show()
}

注解 @JvmName 与 Kotlin

@JvmName 用于Kotlin文件,在Kotlin文件自动生成Java类时,指定类名称
@file:JvmName("指定的类名")
KtBase.kt

@file:JvmName("Stu")

package com.demo.lib

fun showStudentInfo(info: String) = println(info)

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
//        KtBaseKt.showStudentInfo("info");
        Stu.showStudentInfo("info");
    }
}

注解 @JvmField 与 Kotlin

在Kotlin类的属性上增加@JvmField,会剔除自动生成的getXxx()方法,在Java调用该属性时可直接调用
Persons.kt

class Persons {
    @JvmField
    val names = listOf("zhangsan", "lisi", "wangwu")
}

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
        Persons persons = new Persons();
        // 如果不加 @JvmField,只能调用getXxx()方法获取属性
        for (String name : persons.getNames()) {
            System.out.println(name);
        }

        // 加了 @JvmField,可以直接调用属性,不会再生成getXxx()方法
        for (String name : persons.names) {
            System.out.println(name);
        }
    }
}

注解 @JvmOverloads 与 Kotlin

@JvmOverloads 用于Kotlin函数,在Java调用Kotlin函数时,可以使用其定义好的参数默认值
原理是编译器会生成多个传递不同参数的重载方法,给Java用
KtBase.kt

fun show(name: String, age: Int = 20, sex: Char = 'm') {
    println("name: $name, age: $age, sex: $sex")
}

@JvmOverloads
fun toast(name: String, sex: Char = 'm') {
    println("name: $name, sex: $sex")
}

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
        // KtBaseKt.show("zhangsan");  // Java不能享用Kotlin函数的参数默认值
        KtBaseKt.toast("zhangsan"); // Kotlin函数加上@JvmOverloads后,Java可以享用参数默认值
    }
}

注解 @JvmStatic 与 Kotlin

MyObject.kt

class MyObject {

    companion object {
        @JvmField  // 编译时会把TARGET移出Companion内部类
        val TARGET = "公园"

        @JvmStatic // 编译时会把showAction方法移出Companion内部类
        fun showAction(name: String) = println("${name}要去${TARGET}玩")
    }
}

JavaCallKotlin.java

public class JavaCallKotlin {
    public static void main(String[] args) {
        // System.out.println(MyObject.Companion.getTARGET()); // 不加@JvmField,只能通过.Companion调用
        MyObject.Companion.showAction("zhangsan");  // 不加@JvmStatic,只能通过.Companion调用

        System.out.println(MyObject.TARGET);  // 加上@JvmField,可以直接调用,并且会剔除getXxx()方法
        MyObject.showAction("zhangsan");  // 加上@JvmStatic,可以直接调用
    }
}

手写事件变换操作符-create

fun main() {
    create { 
        "zhangsan"
    }
}

class RxCoreClass() {

}

inline fun <OUTPUT> create(action: () -> OUTPUT): RxCoreClass {
    
}

手写事件变换操作符-中转站

fun main() {
    create {
        "zhangsan"
    }
}

class RxCoreClass<T>(valueItem: T) {
    // create 操作符,最后一行的返回值,流向此处
}

inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())

手写事件变换操作符-map

fun main() {
    create {
        "zhangsan"
    }.map {

    }.map {
        
    }
}

// valueItem == create 操作符 最后一行的返回值,流向此处
class RxCoreClass<T>(var valueItem: T)

inline fun <I, O> RxCoreClass<I>.map(mapAction: I.() -> O): RxCoreClass<O> {
    return RxCoreClass(mapAction(valueItem))
}

inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())

手写事件变换操作符-observer

fun main() {
    create {
        123
    }.map {
        "值是:$this"
    }.map { 
        "长度是:$length"
    }.observer {
        println(this)
    }
}

// valueItem == create 操作符 最后一行的返回值,流向此处
class RxCoreClass<T>(var valueItem: T)

inline fun <I, O> RxCoreClass<I>.map(mapAction: I.() -> O): RxCoreClass<O> = RxCoreClass(mapAction(valueItem))

inline fun <I> RxCoreClass<I>.observer(observerAction: I.() -> Unit) = observerAction(valueItem)

inline fun <OUTPUT> create(action: () -> OUTPUT) = RxCoreClass(action())
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 五、Lambda编程 1.Lambda表达式和成员引用 Lambda简介:作为函数参数的代码块。可以理解为简化表达...
    TomyZhang阅读 433评论 0 0
  • 基本类型: 数字 Double Float Long Int Short Byte 没有隐式拓宽转换,但...
    YuanchaoLi阅读 505评论 0 2
  • 在 Kotlin 中的变量、常量以及注释多多少少和 Java 语言是有着不同之处的。下面详细的介绍 Kotlin ...
    驰同学阅读 856评论 0 2
  • 一、定义和目的 1.定义 Kotlin是一种针对Java平台的新编程语言。Kotlin简洁、安全、务实,并且专注于...
    TomyZhang阅读 353评论 0 0
  • Kotlin概述 Kotlin是由开发过IntelliJ IDEA、Android Studio、PyCharm等...
    OKmyself阅读 11,330评论 2 12