推荐阅读:
学习Kotlin(一)为什么使用Kotlin
学习Kotlin(二)基本语法
学习Kotlin(三)类和接口
学习Kotlin(四)对象与泛型
学习Kotlin(五)函数与Lambda表达式
学习Kotlin(六)扩展与委托
学习Kotlin(七)反射和注解
学习Kotlin(八)其他技术
Kotlin学习资料总汇

目录

1.扩展

  • 1.1 扩展函数
  • 1.2 扩展属性
  • 1.3 扩展伴生对象
  • 1.4 扩展的作用域

2.委托

  • 2.1 类委托
  • 2.2 委托属性
  • 2.3 标准委托

1.扩展

在Kotlin中,允许对类进行扩展,不需要继承该类或使用像装饰者这样的任何类型的设计模式,通过一种特殊形式的声明,来实现具体实现某一具体功能。扩展函数是静态解析的,并未对原类增添函数或者属性,对类本身没有影响。

1.1扩展函数

声明一个扩展函数,我们需要用一个接收者类型也就是被扩展的类型来作为他的前缀。 下面代码为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 函数时,可以这样:

val mutableList = mutableListOf(1, 2, 3)
mutableList.swap(1, 2) //调用扩展函数swap()
println(mutableList)

运行代码,得到结果

  • 内部成员函数名与扩展函数名相同
    如果扩展函数与内部成员函数冲突,如下所示:
class User {fun print() {println("内部成员函数")}
}//扩展函数
fun User.print() {println("扩展函数")
}//调用
User().print()

运行代码,得到结果:

由上面例子可得,如果扩展函数的函数名跟内部成员函数的函数名冲突,会优先调用内部成员函数。

  • 可空接收者
    可以为可空的接收者类型定义扩展,如下所示:
//扩展函数
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)

运行代码,得到结果:

上面代码中,我们对可空的接受者定义扩展,检测调用者是否为null,如果为null,返回"null",如果不为null,返回Any.toString()

1.2扩展属性

扩展属性是对属性的扩展,如下所示:

class User {//必须声明为public(Kotlin默认是public)//否则扩展属性无法访问该变量var mValue = 0
}//扩展属性
var User.value: Intget() = mValueset(value) {mValue = value}//调用扩展函数
var user = User()
user.value = 2
println(user.value)

运行代码,得到结果:

上面代码中对 mValue 进行了属性扩展,抵用了扩展属性实现了 setter 方法,对 mValue 进行赋值,再通过扩展属性实现了 getter 方法,获取到 mValue 的值。

1.3扩展伴生对象

除了扩展函数和扩展属性外,还可以对伴生对象进行扩展,代码如下:

class User {companion object {}
}//扩展伴生对象
fun User.Companion.foo() {println("伴生对象扩展")
}//调用
User.foo()

运行代码,得到结果

1.4扩展的作用域

  • 在不同包里进行扩展
    上面的代码都是在同一个包里进行扩展,如果在不同包里要进行扩展,就要用import来导入资源了,如下所示:
package com.demo.czh.otherpackageclass OtherUser {}fun OtherUser.print() {println("其他包的")
}

在其他包中调用

package com.demo.czh.activitydemoimport com.demo.czh.otherpackage.OtherUser
import com.demo.czh.otherpackage.printUser().print()
OtherUser().print()

运行代码,得到结果:

由上面例子可得,如果要在不用的包里进行扩展,要在调用处 import 扩展的资源。

  • 扩展声明为成员
    在一个类内部你可以为另一个类声明扩展,如下所示:
//定义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()}
}

运行代码,得到结果:

上面 User.print() 这个扩展函数中,用到了 限定的 this 语法来调用 User2 的printUser() 函数。

  • 扩展成员的继承
    声明为成员的扩展可以声明为 open 并在子类中覆盖。这意味着这些函数的分发对于分发接收者类型是虚拟的,但对于扩展接收者类型是静态的。
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())

运行代码,得到结果:


2.委托

在Kotlin中,如果有多个地方用到了相同的代码,可以用委托来处理。

2.1类委托

委托模式是实现继承一个很好的的替代方式,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

2.2 委托属性

在实际应用中,有很多类的属性都拥有 getter 和 setter 函数,这些函数大部分都是相同的。Kotlin允许委托属性,把所有相同的 getter 和 setter 函数放到同一个委托类中,这样能大大减少冗余代码。举个例子:

class User1 {var userName: String = ""get() = fieldset(value) {field = value}
}
class User2 {var userName: String = ""get() = fieldset(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 函数,去除了相同的代码。

2.3 标准委托

Kotlin标准库中提供了一些有用的委托函数:

  • 延迟委托
  • 可观察属性委托
  • Map委托

延迟委托

lazy()是接受一个 lambda 表达式作为参数,并返回一个 Lazy <T> 实例的函数,返回的实例作为一个委托,第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 之后再调用 get() 返回记录的结果。

val lazyValue: String by lazy {println("computed!")"Hello"
}//调用两次
println(lazyValue)
println(lazyValue)

运行代码,得到结果:

默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。如下所示:

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委托是指用Map实例自身作为委托来实现委托属性,通常用于解析 JSON ,如下所示:

//新建User类,主构函数要求传入一个Map
class User(val map: Map<String, Any>) {//声明一个 String 委托给 mapval 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 mapvar 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自身支持并非常好用的,扩展能使代码更灵活,委托能实现代码重用。运用好他们能很好地加快编写代码的速度。

原文链接:https://juejin.im/post/5a9648466fb9a0633b213d73

学习Kotlin(六)扩展与委托相关推荐

  1. 学习Kotlin(八)其他技术

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  2. 学习Kotlin(七)反射和注解

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  3. 学习Kotlin(五)函数与Lambda表达式

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  4. 学习Kotlin(四)对象与泛型

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  5. 学习Kotlin(三)类和接口

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  6. 学习Kotlin(二)基本语法

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  7. 学习Kotlin(一)为什么使用Kotlin

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  8. Kotlin学历之扩展与委托

    简述 讲到Kotlin中的扩展,不得不说代码的设计原则之一:开闭原则,意思是对扩展开放,对修改关闭.在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果.所以一句话概括就是:为了使程序 ...

  9. ES6学习(六)—函数的扩展

    ES6学习(六)-函数的扩展 挑重点 文章只列举了一部分 完整版请看阮一峰ES6入门教程 点我查看阮一峰ES6入门教程 一.函数参数的默认值 二.rest 参数 ES6 引入 rest 参数(形式为- ...

最新文章

  1. Swagger增强神器:Knife4j!用它轻松实现接口搜索、Word下载、接口过滤...
  2. javascript全局观
  3. WIKI 开发工具网站收藏
  4. 踵事增华:新形势下如何高效撰写科技论文!
  5. php 输出 jsonp
  6. java step1:基础知识1
  7. [其他] 10种技巧可提升Android应用运行效果
  8. 简单python脚本实例-python常用运维脚本实例
  9. 人脸库对比(百度人脸识别)(Java版)
  10. SONiC testbed
  11. 「技术架构」TOGAF建模:环境和位置图
  12. android 墓碑日志,关于清明节扫墓的日记
  13. php读取写入,php操作文件(读取写入文件)
  14. 解决微信开发者工具的不信任问题
  15. java uint64_Java 中的 uint64
  16. Dense biased networks with deep priori anatomy and hard region adaptation: Semi-supervised learning
  17. 墨尔本大学 SWEN20003 Project2 课业解析
  18. 输入框常规测试数据用例设计
  19. linux文件系统知识总结、SD卡挂载问题总结
  20. 时间间隔感测试器(下):Arduino uno

热门文章

  1. NTU 课程笔记:MAS 714(16) 图灵机
  2. oracle in与exists的使用
  3. 控制台上的内容不输入到nohup.out
  4. CSDN-markdown编辑器使用指南
  5. python中range和xrange的异同
  6. 微服务实战(三):深入微服务架构的进程间通信
  7. Java内存模型深度解析:总结--转
  8. JS问题Uncaught ReferenceError:XXXX is not defined
  9. Java 类的热替换---转载
  10. Python数据结构与算法(第六天)