扩展与委托html
在Kotlin中,容许对类进行扩展,不须要继承该类或使用像装饰者这样的任何类型的设计模式,经过一种特殊形式的声明,来实现具体实现某一具体功能。扩展函数是静态解析的,并未对原类增添函数或者属性,对类自己没有影响。设计模式
声明一个扩展函数,咱们须要用一个接收者类型也就是被扩展的类型来做为他的前缀。 下面代码为Kotlin原生集合类 MutableList 添加一个 swap 函数:安全
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // “this”对应该列表
this[index1] = this[index2]
this[index2] = tmp
}
复制代码
上面代码用MutableList做为接受者类型,对其进行扩展,为其添加一个 swap 函数,当咱们调用 swap 函数时,能够这样:bash
val mutableList = mutableListOf(1, 2, 3)
mutableList.swap(1, 2) //调用扩展函数swap()
println(mutableList)
复制代码
运行代码,获得结果微信
class User {
fun print() {
println("内部成员函数")
}
}
//扩展函数
fun User.print() {
println("扩展函数")
}
//调用
User().print()
复制代码
运行代码,获得结果:ide
//扩展函数
fun Any?.toString(): String {
if (this == null) return "null"
return toString()
}
//调用
var a = null
a.toString()
println(a)
var b = "not null"
b.toString()
println(b)
复制代码
运行代码,获得结果:函数
扩展属性是对属性的扩展,以下所示:post
class User {
//必须声明为public(Kotlin默认是public)
//不然扩展属性没法访问该变量
var mValue = 0
}
//扩展属性
var User.value: Int
get() = mValue
set(value) {
mValue = value
}
//调用扩展函数
var user = User()
user.value = 2
println(user.value)
复制代码
运行代码,获得结果:学习
除了扩展函数和扩展属性外,还能够对伴生对象进行扩展,代码以下:ui
class User {
companion object {
}
}
//扩展伴生对象
fun User.Companion.foo() {
println("伴生对象扩展")
}
//调用
User.foo()
复制代码
运行代码,获得结果
package com.demo.czh.otherpackage
class OtherUser {
}
fun OtherUser.print() {
println("其余包的")
}
复制代码
在其余包中调用
package com.demo.czh.activitydemo
import com.demo.czh.otherpackage.OtherUser
import com.demo.czh.otherpackage.print
User().print()
OtherUser().print()
复制代码
运行代码,获得结果:
//定义User类,添加一个printUser()函数
class User {
fun printUser(){
println("User")
}
}
//定义User2类,在里面对User类进行扩展
class User2 {
fun printUser2() {
println("User2")
}
//扩展函数
fun User.print() {
printUser()
printUser2()
}
fun getUser(user: User) {
//调用扩展函数
user.print()
}
}
//调用
User2().getUser(User())
复制代码
运行代码,获得结果:
//User类不变
class User {
fun printUser(){
println("User")
}
}
//User2
class User2 {
fun printUser() {
println("User2")
}
fun User.print() {
printUser()
//表示调用User2的printUser()函数
this@User2.printUser()
}
fun getUser(user: User) {
//调用扩展方法
user.print()
}
}
复制代码
运行代码,获得结果:
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println("D.foo in C")
}
open fun D1.foo() {
println("D1.foo in C")
}
fun caller(d: D) {
d.foo() // 调用扩展函数
}
fun caller2(d1: D1) {
d1.foo() // 调用扩展函数
}
}
class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}
override fun D1.foo() {
println("D1.foo in C1")
}
}
//调用
C().caller(D())
C1().caller(D())
C().caller(D1())
C().caller2(D1())
C1().caller2(D1())
复制代码
运行代码,获得结果:
在Kotlin中,若是有多个地方用到了相同的代码,能够用委托来处理。
委托模式是实现继承一个很好的的替代方式,Kotlin支持委托模式,不用为了实现委托模式而编写样板代码。举个例子:
//定义一个接口 Base
interface Base {
fun print()
}
//定义一个 ImplBase 实现接口 Base
class ImplBase(val i: Int) : Base {
override fun print() {
println(i)
}
}
//定义一个 Drived 类实现接口 Base
class Drived(b: Base) : Base {
//这里须要 override 接口 Base 里的方法
override fun print() {
}
}
//若是使用委托模式的话,能够把 Base 里的方法委托给 Drived
class Drived(b: Base) : Base by b
//调用 print() 方法
var b = ImplBase(10)
Drived(b).print()
//运行代码,打印结果为 10
复制代码
从上面代码能够看出,Derived 类经过使用 by 关键字将 Base 接口的 print 方法委托给对象 b ,若是不进行委托的话,则要 override Base 接口的 print 方法。 若是出现委托后仍然 override 的状况,编译器会使用你的 override 实现取代委托对象中的实现,以下所示:
//委托后仍然 override
class Drived(b: Base) : Base by b {
override fun print() {
println("abc")
}
}
//调用 print() 方法
var b = ImplBase(10)
Drived(b).print()
//运行代码,打印结果为 abc
复制代码
在实际应用中,有不少类的属性都拥有 getter 和 setter 函数,这些函数大部分都是相同的。Kotlin容许委托属性,把全部相同的 getter 和 setter 函数放到同一个委托类中,这样能大大减小冗余代码。举个例子:
class User1 {
var userName: String = ""
get() = field
set(value) {
field = value
}
}
class User2 {
var userName: String = ""
get() = field
set(value) {
field = value
}
}
复制代码
User1和User2都有相同的 getter 和 setter 函数,把它们放到委托类中,以下:
//定义一个委托类Delegate
class Delegate {
var userName = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("getValue 类名:$thisRef, 属性名:${property.name}")
return userName
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("setValue 类名:$thisRef, 属性名:${property.name},值:$value")
userName = value
}
}
//将 userName 委托给 Delegate
class User1 {
var userName: String by Delegate()
}
class User2 {
var userName: String by Delegate()
}
//调用 getter 和 setter 函数
var user1 = User1()
user1.userName = "user1"
println(user1.userName)
var user2 = User2()
user2.userName = "user2"
println(user2.userName)
复制代码
运行代码,获得结果:
能够看到,User1 和 User2 都将 userName 委托给 Delegate ,在 Delegate 内完成 getter/setter 函数,去除了相同的代码。
Kotlin标准库中提供了一些有用的委托函数:
lazy()
是接受一个 lambda 表达式做为参数,并返回一个 Lazy <T>
实例的函数,返回的实例做为一个委托,第一次调用 get()
会执行已传递给 lazy()
的 lambda 表达式并记录结果, 以后再调用 get()
返回记录的结果。
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
//调用两次
println(lazyValue)
println(lazyValue)
复制代码
运行代码,获得结果:
val lazyValue: String by lazy(LazyThreadSafetyMode.PUBLICATION ) {
"Hello"
}
复制代码
而若是你肯定初始化将老是发生在单个线程,那么你可使用 LazyThreadSafetyMode.NONE 模式, 它不会有任何线程安全的保证和相关的开销,以下所示:
val lazyValue: String by lazy(LazyThreadSafetyMode.NONE) {
"Hello"
}
复制代码
实现可观察属性委托的函数是Delegates.observable()
,当咱们使用该委托函数时,能够观察属性的变化,以下所示:
var name: String by Delegates.observable("Czh") { property, oldValue, newValue ->
println("属性名:$property 旧值:$oldValue 新值:$newValue")
}
//修改name的值
name = "abc"
name = "hello"
复制代码
运行代码,获得结果:
Delegates.observable()
接收两个参数,第一个是初始值,第二个是修改时处理程序(handler)。 每当咱们给属性赋值时会调用该处理程序,他有三个参数,第一个是被赋值的属性,第二个是旧值,第三个是新值。 若是想拦截属性的赋值操做,而且否决他的赋值操做,能够用
vetoable()
取代
observable()
,传递给
vetoable()
的修改时处理程序会返回一个boolean类型,若是返回true,容许赋值,返回false则反之。以下所示:
var name: String by Delegates.vetoable("Czh") { property, oldValue, newValue ->
if (newValue.equals("abc")) {
println("属性名:$property 旧值:$oldValue 新值:$newValue")
true
} else {
println("不能修改成除了abc之外的值")
false
}
}
//修改name的值
name = "abc"
name = "hello"
复制代码
运行代码,获得结果:
Map委托是指用Map实例自身做为委托来实现委托属性,一般用于解析 JSON ,以下所示:
//新建User类,主构函数要求传入一个Map
class User(val map: Map<String, Any>) {
//声明一个 String 委托给 map
val name: String by map
//由于 Map 为只读,因此只能用 val 声明
val age: Int by map
}
var map = mapOf("name" to "Czh", "age" to 22)
var user = User(map)
println("${user.name} ${user.age}")
//打印结果为 Czh 22
复制代码
由于Map
只有getValue
方法而没有setValue
方法,因此不能经过User对象设置值,这时能够把User的主构函数改成传入一个MutableMap
,并把属性委托给MutableMap
,以下所示:
class User(val map: MutableMap<String, Any>) {
//由于MutableMap为读写,能够用var声明
var name: String by map
var age: Int by map
}
var map = mutableMapOf("name" to "Czh", "age" to 22)
var user = User(map)
user.name = "James Harden"
user.age = 28
println("${user.name} ${user.age}")
//打印结果为 James Harden 28
复制代码
本篇文章简述了Kotlin中扩展和委托的使用方法。扩展和委托都是Kotlin自身支持并不是常好用的,扩展能使代码更灵活,委托能实现代码重用。运用好他们能很好地加快编写代码的速度。
参考文献:
Kotlin语言中文站、《Kotlin程序开发入门精要》
推荐阅读:
从Java到Kotlin(一)为何使用Kotlin
从Java到Kotlin(二)基本语法
从Java到Kotlin(三)类和接口
从Java到Kotlin(四)对象与泛型
从Java到Kotlin(五)函数与Lambda表达式
从Java到Kotlin(六)扩展与委托
从Java到Kotlin(七)反射和注解
从Java到Kotlin(八)Kotlin的其余技术
Kotlin学习资料总汇
更多精彩文章请扫描下方二维码关注微信公众号"AndroidCzh":这里将长期为您分享原创文章、Android开发经验等! QQ交流群: 705929135