Skip to content

Kotlin 上路

Posted on:March 17, 2020 at 23:50:14 GMT+8

开始

对一个对象实例调用多个方法(使用 with)

class Turtle {
    fun penDown()
    fun penUp()
    fun turn(degrees: Double)
    fun forward(pixels: Double)
}

val myTurtle = Turtle()
with(myTurtle) { // 画一个 100 像素的正方形
    penDown()
    for(i in 1..4) {
        forward(100.0)
        turn(90.0)
    }
    penUp()
}

配置对象的属性(使用 apply)

val myRectangle = Rectangle().apply {
    length = 4
    breadth = 5
    color = 0xFAFAFA
}

类与对象

属性与字段

幕后字段

var counter = 0 // Note: the initializer assigns the backing field directly
    set(value) {
        if (value >= 0) field = value
    }

value的值指定counter的值。

幕后属性

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap()
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

可见性修饰符

类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有可见性修饰符(getter 与属性有相同的可见性)。

Kotlin 中有 private protected internal public 四个可见性修饰符,默认是public

internal 模块内可见。

注意:Kotlin 中,外部类不能访问到内部类的private成员。

对象声明

即一个关键字object 实现单例。

伴生对象

companion关键字声明伴生对象,即类内部的对象。

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

委托

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}

可以看到Derived类相比普通的声明多了by b这部分,这部分的意思就是说b的公有成员都委托给Derived

委托属性

标准委托

Kotlin 标准库中内置了几种委托。

延迟属性 Lazy

lazy()接受一个 lambda 并返回一个Lazy<T>的实例,返回的实例作为延迟属性的委托。第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只返回记录的结果。

可观察属性 Observable
import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println(prop)
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}

Delegates.observable()接受两个参数初始值inintalValue和值改变时的回调方法onChangeonChange接受三个参数:被赋值的属性,旧值,新值。在回调方法被调用前,值已经改变过了。

image.png

如果需要拦截新值并否决,可以使用vetoable()代替observable(),在新值生效前会调用回调方法。

把属性储存在 map 中
class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

fun main() {
    val user = User(mapOf(
        "name" to "John Doe",
        "age"  to 25
    ))
    println(user.name) // Prints "John Doe"
    println(user.age)  // Prints 25
}

函数

函数可以有默认参数。当覆盖(重写)一个有默认参数的函数时,新函数不可以设置默认值。

当向函数传递参数时,可以指定参数的名称。

函数的可变参数(通常是最后一个)可以用 vararg 修饰符标记。当把一个数组a传给可变参数,可以用*a(伸展操作符 Spread )。

中缀表示函数:

infix fun Int.shl(x: Int): Int { …… }

// 用中缀表示法调用该函数
1 shl 2

// 等同于这样
1.shl(2)

高阶函数和 Lambda 表达式

高阶函数是将函数用作参数或返回值的函数。

Kotlin 中,如果一个函数的参数的最后一个参数为函数,则这个函数参数可以放在()外,写在{}里。更甚,如只有一个参数且为函数,则()也可省略。

Lambda 表达式或者匿名函数(以及局部函数对象表达式) 可以访问其 闭包 ,即在外部作用域中声明的变量。 在 lambda 表达式中可以修改闭包中捕获的变量:

var sum = 0
ints.filter { it > 0 }.forEach {
    sum += it
}
print(sum)

内联函数

inline修饰符会影响函数本身和传给它的 lambda 表达式,它们被内联到调用处,可以提高一些效率。

inline fun <T> lock(lock: Lock, body: () -> T): T { …… }
lock(l) { foo() }
l.lock()
try {
    foo()
}
finally {
    l.unlock()
}

扩展函数

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}
val list = mutableListOf(1, 2, 3)
list.swap(0, 2)

作用域函数

函数对象引用返回值是否是扩展函数
letitLambda 表达式结果
runthisLambda 表达式结果
run-Lambda 表达式结果不是:调用无需上下文对象
withthisLambda 表达式结果不是:把上下文对象当做参数
applythis上下文对象
alsoit上下文对象

协程

参考

Reference - Kotlin 语言中文站

2020-3-17 23:50:39