KotLin 相关文档
官方在线Reference
kotlin-docs.pdf
Kotlin for android Developers 中文翻译
Kotlin开发工具集成,相关平台支持指南
Kotlin开源项目与Libraries
Kotlin开源项目、资源、书籍及课程搜索平台
Google’s sample projects written in Kotlin
Kotlin and Android
Kotlin委托机制
委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。
委托类 (Class Delegation)
例,
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() {
print(x)
}
}
class Derived(b: Base) : Base by b {
fun add() {
}
}
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
Derived(b).add()
}
在基类列表上使用by
子句,表示,Derived类的内部对象b,会生成Base的所有公共函数。这时调用Derived的属于Base的公共函数,就会委托调用b对象的对应函数。
Derived是一个委托类,其对应的委托实例就是构造函数中Base的实例b
委托属性 (Delegated Properties)
有一些常见的属性,虽然我们可以每次需要时手动实现它们,但现在只需要如下形式的使用,即可实现。这些属性,如:
-
lazy properties 只在第一次访问时进行值计算
-
observable properties 监听获取关于属性变化的通知
-
storing properties
将属性存储在一个Map中
委托属性语法
例,
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
class Example {
var p: String by Delegate()
}
fun main(args: Array<String>) {
val example = Example()
example.p = "stone"
println(example.p)
}
输出:
stone has been assigned to 'p in com.stone.clazzobj.delegation.Example@238e0d81.'
com.stone.clazzobj.delegation.Example@238e0d81, thank you for delegating 'p' to me!
委托属性语法:
val/var <property name>: <Type> by <expression>
如,上例中的Example的属性p
这里将属性p的get/set委托给Delegate类的getValue/setValue函数来处理。
属性委托类,如Delegate类,其可以在形式上继承/实现一个基类/接口(这里指非特定的),然而该接口的函数在外部并不能由委托属性调用,所以继承/实现的特性,在这里并没有什么大的意义
标准委托属性
Kotlin标准库,为如下几种有用的委托形式,提供了相应的工厂方法
- lazy
val lazyValue: String by lazy {
println("computed!") //多次调用 只会输出一次
"Hello"
}
查看lazy源码,发现它调用的是:
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
SynchronizedLazyImpl这是一个同步执行的函数,内部实现是加了synchronized的同步代码块的;所以在多线程环境中,第一个进入同步代码块执行出的结果,就是lazy-property的值。
再看lazy的一个重载方法:
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
使用,如
val lazyValue: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {...}
LazyThreadSafetyMode对应的三种模式: a. SYNCHRONIZED 同步
b. PUBLICATION 发布。内部的计算过程可能被多次访问,但内部的属性是一个Volatile修饰的属性,所以在多线程环境中,被第一次访问获取数据后,此后的其它线程都共享该值。
c. NONE
未对多线程环境,做任何处理。
所以在多线程环境中使用,会造成:计算和返回值都可能处在多个线程中。
注:lazy属性,只能声明为 val的,即它是只能get的
- Observable
需要在属性后跟by Delegates.observable()
,即调用这个方法设定属性值。该方法是两个参数:初始值,lambda表达式(即处理值的语句块)。
例,
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
property, oldValue, newValue ->
println("$oldValue -> $newValue") }
}
fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
输出:
<no name> -> first
first -> second
Delegates.observable()中,使用的lambda表达式,其参数:
I. property:在这指属性name
II. oldValue: 旧值,首次即为指定的初始值
III. newValue:新set的值
最终属性值=new值
还有个Delegates.vetoable(),跟Delegates.observable()类似,只是lambda表达式的返回值为Boolean。
返回true:属性值=new值
返回false:属性值=old值observable 属性,可以定为val的,不过既然要监听属性的变化,还是用var较好
- Storing Properties in a Map
将属性委托给(存进)一个Map。
例,
class Per(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
val per = Per(mapOf("name" to "John Doe", "age" to 25))
println(per.name)
println(per.age)
}
输出:
John Doe
25
也可以使用一个
var map
,即map的类型为MutableMap
局部委托属性
在kotlin1.1后,可以定义局部的委托属性。如,
class Foo {
fun isValid(): Boolean {
return Random().nextBoolean()
}
fun doSomething() {
println("doSomething")
}
}
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo) //memoizedFoo: Foo
if (memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
// var p: String by Delegate() //可以有其它形式的委托属性
}
fun main(args: Array<String>) {
example {
println("生成Foo")
Foo()
}
}
上例的example(),接受一个函数参数,该函数返回Foo类型对象。
memoizedFoo,就是一个局部委托属性;这里委托的是public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
这个函数,该lazy函数接受一个函数参数。
局部委托属性也可以是其它的委托属性形式。
委托属性给一个类实例之总结
-
对于一个val修饰的只读属性
它的委托必须提供一个名为
getValue
的函数。该函数中的两个参数的意义,如下
thisRef : 其类型,必须是委托属性的所有者或其super类型
property : 必须是KProperty<*>
或其super类型KCallable<*>
-
对于一个var修饰的可变属性
其含有参数,除了上述两个外,还多一个new-value值
注意getValue/setValue函数的声明以operator开始 这两个函数,可以直接定义成委托类的成员函数,或委托类的扩展函数。后者对于在原始类中未提供它们时,提供了方便
-
ReadOnlyProperty 和 ReadWriteProperty
属性的委托类,可以实现两个接口,kotlin.properties.ReadOnlyProperty
和kotlin.properties.ReadWriteProperty
;这两个接口中,声明了getValue(var和val属性)/setValue(var属性)函数。例,
class Example1: Super() {
val p1: String by Delegate1()
}
class Delegate1: ReadOnlyProperty<Example1, String> {
override fun getValue(thisRef: Example1, property: KProperty<*>): String {
return ""
}
}
不实现这两个接口也是可以的,如之前使用 operator声明的getValue/setValue函数
委托属性转化规则
声明的委托属性,会由编译器产生一个隐藏属性,且会生成对应的get/set方法。
如,
class C {
var prop: Type by MyDelegate()
}
在编译器中,会产生如下代码:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
provideDelegate函数提供委托实例
在kotlin1.1,提供了provideDelegate函数,它可以为属性提供对象委托逻辑。
如果 by 右侧所使用的对象将 provideDelegate(
函数名不能随意变更) 定义为成员或扩展函数,那么会调用该函数来 创建属性委托实例;且要求委托实例的类,要实现 ReadOnlyProperty 或 ReadWriteProperty接口
例,
class Dele(val id: String): ReadOnlyProperty<MyUI, String> {
override fun getValue(thisRef: MyUI, property: KProperty<*>): String {
return "${UUID.randomUUID()}_$id"
}
}
class ResourceID {
private constructor(id: String) {
this.id = id
}
open val id: String
companion object {
val image_id = ResourceID("1")
val text_id: ResourceID = ResourceID("2")
}
}
class ResourceLoader(resId: ResourceID) {
private val resourceID = resId
operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, String> {
checkProperty(thisRef, prop.name)
// create delegate
return Dele(resourceID.id+"")
}
private fun checkProperty(thisRef: MyUI, name: String) {
if (name.equals("image")) {
//...
}
}
}
fun MyUI.bindResource(id: ResourceID): ResourceLoader {
return ResourceLoader(id)
}
class MyUI {
val image by bindResource(ResourceID.image_id) //bindResource()产生委托对象
val text by bindResource(ResourceID.text_id)
}
fun main(args: Array<String>) {
println("image-id = " + MyUI().image)
println("txt-id = " + MyUI().text)
}
上例中,找到by关键字,其右侧调用了bindResource创建委托类实例。
bindResource(这是一个扩展函数)返回ResourceLoader类型的实例。
ResourceLoader中定义了一个operator声明的函数,名为provideDelegate,其返回真正的委托类Dele的实例对象。
Dele中,getValue函数,返回一个”拼接了UUID前缀+资源类型id”的字符串。
使用provideDelegate函数的委托属性转化规则
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler
// when the 'provideDelegate' function is available:
class C {
// calling "provideDelegate" to create the additional "delegate" property
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
val prop: Type
}