Kotlin-语法

语法

  1. 中缀表达式

    1
    2
    3
    4
    // 声明
    infix fun <A, B> A.to(that : B): Pair<A, B>
    // 使用: A 中缀方法 B
    1 to 2
  2. 可变参数

    1
    2
    3
    4
    // varargs,类似于java的 ... ,但是位置没有限制
    // 可以使用 * 来传入外部的变量作为可变参数的变量
    val letters = arrayof("a", "b", "c")
    printLetters(*letters)
  3. 原生字符串

    1
    2
    3
    4
    val str = """
    123
    abc
    """
  4. 判断

    • 结构相等:== ,判断内容是否相等
    • 引用相等:=== , 判断引用是否一样
  5. 区间

    1
    2
    3
    4
    5
    // 区间[IntRange]
    1 .. 10 // [1,...,10]
    1 until 10 // [1,...,9)
    10 downTo 1 // [10,9...1]
    1 .. 10 step 2 // [1,3,5...]
  6. 遍历

    1
    2
    3
    4
    // 遍历
    for (i in array) {}
    // 获取索引
    for (i in array.indices) {}
  7. 延迟初始化:by lazy

    1
    2
    3
    4
    5
    6
    // 默认线程安全
    val str by lazy { "str" }
    // 并行模式
    val str by lazy(LazyThreadSafetyMode.PUBLICATION) { "str" }
    // 不做任何线程安全保证,也不会有任何线程开销
    val str by lazy(LazyThreadSafetyMode.NONE) { "str" }
    • 变量必须是不可以变的:val
    • 在首次调用时才会初始化
    • lazy属性会默认加入同步锁,在同一时刻只允许一个线程堆lazy属性进行初始化
  8. 函数

    1
    2
    3
    4
    // lambda 表达式
    val foo = {x: Int, y: Int -> x + y} // foo.invoke(1, 2) 或 foo(1, 2)
    // lambda 表达式函数体
    fun foo(x: Int) = {y: Int -> x + y} // foo(1).invoke(2) 或 foo(1)(2)
  9. 匿名内部类

    1
    2
    3
    4
    5
    6
    7
    8
    // lambda 表达式
    val runnable = Runnable{}
    // 匿名实现类
    val runnable = object : Runnable {
    override fun run() {}
    }
    // 或匿名内部类
    executor.submit(object:Runnable{...})
  1. 密封类:sealed

    1
    2
    3
    sealed class State
    class SuccessStatus:Status
    class ErrorStatus:Status
    • 要继承必须将子类定义在同一个文件中
  2. 内联类

    类似于包装类型,在编译时会把实际值替换到调用处
    由于 Kotlin 不能使用IntDef模拟枚举的调用,可以考虑内联类来模拟枚举

    1
    2
    3
    4
    5
    6
    inline class State(val ordinal:Int)
    companion object {
    // 模拟枚举
    val Idle = State(0)
    val Busy = State(1)
    }
  3. 可见性修饰符

    修饰符 含义 与Java比较
    public Kotlin中默认修饰符,全局可见 与Java中public效果相同
    protected 受保护修饰符,类及子类可见 含义一致,作用域除了类和子类,包内也可见
    private 私有修饰符,类内修饰只有本类可见,类外修饰文件内可见 私有修饰符,只有类内可见
    internal 模块内可见
  4. 内部类和静态内部类

    1
    2
    3
    4
    class OutClass{
    class StaticClass{} // 静态内部类
    inner class InnerClass{} // 内部类
    }
  5. 委托代替接口实现

    1
    2
    3
    4
    5
    6
    7
    8
    interface CanFly {
    fun fly()
    }
    open class Flyer : CanFly {
    override fun fly() {
    }
    }
    class Bird(flyer: Flyer) : CanFly by flyer
  6. 解构

    普通类想要使用结构方式可以手动写component1 component2一一对应的结构函数

    1
    2
    3
    4
    5
    // 数据类最多支持五个参数的解构
    val pair = Pair(1, 2)
    val triple = Triple(1, 2, 3)
    val (n1, n2) = pair
    val (a, b, c) = triple
  7. 伴生类

    1
    2
    3
    4
    5
    class OutClass{
    companion object { //相对于饿汉式静态内部类,全局只有一个实例
    fun testFun() {}
    }
    }
  8. object 单例

    1
    object Child

    相当于

    1
    2
    3
    4
    5
    6
    7
    8
    public final class Child {
    @NotNull
    public static final Child INSTANCE;
    private Child() {}
    static {
    INSTANCE = new Child();
    }
    }
  9. 操作符

    1
    2
    3
    4
    5
    6
    var obj:TestData? = null
    val result = obj?.value ?: -1 // 如果 obj 为空,则返回 1
    val result = obj!!.value // 明确知道obj不为空

    val stu: Student? = getStu() as Student // 强制转换,如果为空会报错
    val stu: Student? = getStu() as? Student // 强制转换,如果 getStu 返回空,则转换为空
  10. 泛型内联特化

    1
    2
    3
    4
    5
    inline fun <reified T> method(t:T) {
    val ts = Array<T>(3){}
    val jclass = T::class.java
    val list = ArrayList<T>()
    }
  11. 数组

    1
    2
    3
    val a1 = arrayOf(o1, o2, o3) // 普通数组

    val a2 = intArrayOf(1, 2, 3) // 基本数据类型数组:longArrayOf、floatArrayOf、doubleArrayOf....
  12. 泛型多继承

    1
    2
    3
    4
    5
    interface Ground
    open class Fruit
    class Water: Fruit(),Ground

    fun <T> cut(t: T) where T : Fruit, T : Ground {}
  13. 作用域操作符

    • 如果需要返回自身的:apply{this} 或 also{it}
    • 不需要返回自身的:run{this}或let{it}
    • 满足条件才执行:data.takeIf { it.age >= 18 }.let {...}
    • 不满足条件才执行:data.takeUnless { it.age >= 18 }.let {...}
  14. withapply

    1
    2
    inline fun <T, R> with(receiver: T, block: T.() -> R): R
    inline fun <T> T.apply(block: T.() -> Unit): T
  15. 惰性求值:序列

    1
    2
    3
    4
    5
    6
    val list = listOf(1, 2, 3, 4, 5)

    // 该操作会产生两个临时集合来记录元素,如果元素非常多时,就会有效率问题
    list.filter {it > 2}.map {it * 2}
    // 通过惰性求值减少临时集合的情况
    list.asSequence().filter {it > 2}.map {it * 2}.toList()
  16. 无限序列

    1
    2
    val naturalNumList = generateSequence(0) {it + 1}
    naturalNumList.takeWhile {it <= 9}.toList()
  17. 内联函数

    内联函数能将函数内容编译到调用处,减少函数调用

    1
    2
    3
    4
    5
    inline fun cost(block:() -> Unit)
    // crossinline 禁止传递的block函数调用 r eturn 方法
    inline fun cost(crossinline block:() -> Unit)
    // noinline 该函数参数不做内联
    inline fun cost(noinline block:() -> Unit)

    内联函数注意事项

    • 由于JVM对普通函数有内联优化,所以普通函数比一定要采用内联函数,这样只会增加复杂性
    • 尽量避免有大量函数体的函数进行内联,这样会增加字节码数量
    • 内联函数无法使用闭包成员【闭包:内部函数持有外部函数变量】
  18. 运算符重载

    参考:https://kotlinlang.org/docs/reference/operator-overloading.html
    operator fun 类名.重载符号(一个参数):返回值 {}

  19. 扩展函数

    1
    2
    3
    4
    5
    class Person{...}
    // 为Person类增加名为ext的扩展方法
    fun Person.ext(s:Any) {
    println(s)
    }
  20. 扩展属性

    1
    2
    val Data.extValue:boolean
    get() = this.otherValue % 2 == 0
  21. 静态扩展函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Son {
    companion object {
    val age = 10
    }
    }
    fun Son.Companion.foo() {
    println("age = $age")
    }
    // 这样就可以不创建类的情况下直接调用
    Son.foo()
  22. 代理观察者

    1
    2
    3
    4
    5
    6
    7
    class Data() {
    var price: Int by Delegates.observable(0, { property, oldValue, newValue ->
    // 当price的值变更时触发该函数
    })
    }
    val data = Data()
    data.price = 100
  23. 代理限制

    1
    2
    3
    4
    5
    6
    7
    8
    class Data() {
    var price: Int by Delegates.vetoable(0, { property, oldValue, newValue ->
    newValue > 0
    })
    }
    val data = Data()
    data.price = 100
    data.price = -100 // 该值不会设置成功
  24. 原始方式创建协程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    suspend {
    // coroutine heandle!
    }.createCoroutine(object : Continuation<Unit> {
    override val context: CoroutineContext
    get() = Dispatchers.Default

    override fun resumeWith(result: Result<Unit>) {
    // Coroutine end!
    }
    }).resume(Unit)
  25. 顶层协程

    1
    2
    val job = GlobalScope.launch {}
    job.cancel()
  26. 会阻塞当前线程直到协程作用域内的代码全部执行完

    1
    runBlocking {}
  27. 创建子协程,必须在协程作用域内使用

    1
    2
    val job = launch{}
    job.cancel()
  28. 挂起函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 没法使用launch函数
    suspend fun name(){}

    // 通过coroutineScope生成带协程作用域的挂起函数
    suspend fun name()=coroutineScope{
    launch{}
    }

    // 注意:coroutineScope也会阻塞当前协程,必须得里面的子协程执行完了才行
    runBlocking {
    coroutineScope {
    launch {}
    }
    // 得等coroutineScope执行完了才会走到这
    }
    // 得等runBlocking执行完了才会走到这
  29. 协程统一取消

    1
    2
    3
    4
    5
    6
    val job = Job()
    val scope = CoroutineScope(job)
    scope.launch{
    // 协程域
    }
    job.cancel()
  30. 超时协程

    1
    withTimeout(100){}
  31. 返回结果的协程域

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    val r1 = async{
    // 返回deferred时协程就已经开始跑了
    }
    val r2 = async{
    // 返回deferred时协程就已经开始跑了
    }
    println("${r1.await()} - ${r2.await()}")// 阻塞返回结果


    // async简化版
    val result = withContext(Dispatchers.Dafault) {} // 阻塞返回结果,其中Dispatchers.Dafault为低并发策略
  32. suspendCoroutine结合回调函数,需要在协程域和挂起函数中使用

    1
    2
    3
    4
    5
    6
    7
    8
    suspend fun request(address:String):String {
    return suspendCoroutine {continuation ->
    new Thread({
    continuation.resume(结果)// 成功
    continuation.resumeWithException(结果)// 失败
    }).start()
    }
    }
  33. 同步代码块和同步代码函数

    1
    2
    3
    4
    5
    6
    7
    fun method2() {
    synchronized(this) {
    }
    }

    @Synchronized fun method1() {
    }
  34. volatile

    1
    @Volatile private var count = 1
  35. ReentrantLock

    1
    2
    val lock = ReentrantLock()
    lock.withLock { method() }
  36. 反射

    首次使用会比java的慢

    1
    2
    Kotlin classString::class 	String::class.java.kotlin
    Java class: String::class.java
  37. 注解变更java调用命名

    1
    2
    3
    4
    5
    6
    7
    @file:JvmName("name")//作用于文件名
    @get:JvmName("name")//作用于变量get函数

    // 带默认参数函数适配java调用
    @JvmOverloads//这样就会生成重载方法来适配默认参数

    inernal // 声明的对象不想被java访问到,可以使用@JvmName("%abc")等特殊字符声明,这样java就被限制了