Kotlin基础-扩展

一、定义

Kotlin 在不修改类 / 不继承类的情况下,向一个类添加新函数或者新属性,更符合开闭原则。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。

  • 扩展属性:定义在类或者kotlin文件中,不允许定义在函数中;
  • 扩展函数:扩展函数可以在已有类中添加新的方法,不会对原类做修改,扩展函数定义形式:
fun receiverType.functionName(params){
    body
}
//receiverType:表示函数的接收者,也就是函数扩展的对象
//functionName:扩展函数的名称
//params:扩展函数的参数,可以为NULL

扩展的本质:扩展函数是定义在类外部的静态函数,函数的第一个参数是接收者类型的对象。这意味着调用扩展时不会创建适配对象或者任何运行时的额外消耗。

二、扩展函数

1、扩展函数是静态解析的,并不是接收者类型的虚拟成员

在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的:

open class C

class D: C()

fun C.foo() = "c"   // 扩展函数 foo

fun D.foo() = "d"   // 扩展函数 foo

fun printFoo(c: C) {
    println(c.foo())  // 类型是 C 类
}

fun main(arg:Array<String>){
    printFoo(D())
}
//实际输出c
2、若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数。
3、扩展一个空对象

在扩展函数内, 可以通过 this 来判断接收者是否为 NULL,这样,即使接收者为 NULL,也可以调用扩展函数。

fun Any?.toString(): String {
    if (this == null) return "null"
    // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
    // 解析为 Any 类的成员函数
    return toString()
}

三、扩展属性

扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。

val Foo.bar = 1 // 错误:扩展属性不能有初始化器

扩展属性只能被声明为 val

四、伴生对象的扩展

如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义扩展函数和属性。
伴生对象通过"类名."形式调用伴生对象,伴生对象声明的扩展函数,通过用类名限定符来调用:

class MyClass {
    companion object { }  // 将被称为 "Companion"
}

fun MyClass.Companion.foo() {
    println("伴随对象的扩展函数")
}

val MyClass.Companion.no: Int
    get() = 10

伴生对象内的成员相当于 Java 中的静态成员,其生命周期伴随类始终,在伴生对象内部可以定义变量和函数,这些变量和函数可以直接用类名引用。
对于伴生对象扩展函数,有两种形式,一种是在类内扩展,一种是在类外扩展,这两种形式扩展后的函数互不影响(甚至名称都可以相同),即使名称相同,它们也完全是两个不同的函数,并且有以下特点:
(1)类内扩展的伴随对象函数和类外扩展的伴随对象可以同名,它们是两个独立的函数,互不影响;
(2)当类内扩展的伴随对象函数和类外扩展的伴随对象同名时,类内的其它函数优先引用类内扩展的伴随对象函数,即对于类内其它成员函数来说,类内扩展屏蔽类外扩展;
(3)类内扩展的伴随对象函数只能被类内的函数引用,不能被类外的函数和伴随对象内的函数引用;
(4)类外扩展的伴随对象函数可以被伴随对象内的函数引用;

五、扩展的作用域

通常扩展函数或属性定义在顶级包下;
要使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用;

六、扩展声明为成员

  • 在一个类内部你可以为另一个类声明扩展。
  • 在这个扩展中,有个多个隐含的接受者,其中扩展方法定义所在类的实例称为分发接受者,而扩展方法的目标类型的实例称为扩展接受者。
  • 以成员的形式定义的扩展函数, 可以声明为 open , 而且可以在子类中覆盖. 也就是说, 在这类扩展函数的派 发过程中, 针对分发接受者是虚拟的(virtual), 但针对扩展接受者仍然是静态的。
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()   // 调用扩展函数
    }
}

class C1 : C() {
    override fun D.foo() {
        println("D.foo in C1")
    }

    override fun D1.foo() {
        println("D1.foo in C1")
    }
}


fun main(args: Array<String>) {
    C().caller(D())   // 输出 "D.foo in C"
    C1().caller(D())  // 输出 "D.foo in C1" —— 分发接收者虚拟解析
    C().caller(D1())  // 输出 "D.foo in C" —— 扩展接收者静态解析
}


//实例执行输出结果为:
//D.foo in C
//D.foo in C1
//D.foo in C

七、内置扩展函数

扩展函数是 Kotlin 用于简化一些代码的书写产生的,其中有 五个函数

  • let
  • with
  • run
  • apply
  • also

1、let 函数

  1. 在函数块内可以通过 it 指代该对象。
  2. 返回值为函数块的最后一行或指定return表达式。
//一般写法
val result = "hello".let {
  println(it.length)
  1000
}

使用场景:
1、使用let函数处理需要针对一个可null的对象统一做判空处理;
2、又或者是需要去明确一个变量所处特定的作用域范围内可以使用;

2、with 函数

  1. 前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。
  2. 返回值为函数块的最后一行或指定return表达式。
//一般写法
var result = with(Person()) {
    println(this.name + this.age)
    1000
}

使用场景:
1、适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上

3、run 函数

  1. 实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回。
  2. 返回值为最后一行的值或者指定的return的表达式。
var result = person.run {
    println("$name + $age")
    1000
}

使用场景:
1、适用于let,with函数任何场景。
因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理

4、apply 函数

  1. 从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样
  2. 返回值是传入对象的本身
val person = Person().apply {
    name = "liming"
    age = 50
}

使用场景:
整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。
apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到

5、also 函数

  1. also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。
  2. 返回值是传入对象的本身。
val result = "hello".also {
    println(it.length)
}

使用场景:
适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用。

区别

方法名 是否有it 是否有this return类型
let × 最后一句
also × this
apply × this
run × 最后一句
with × 最后一句
为什么run、with、apply用this,also 和 let 用it?
  • run、with、apply 函数中的参数 block 是 「T 的扩展函数」,所以采用 this 是扩展函数的接收者对象(receiver)。另外因为 block 没有参数,所以不存在 it 的定义。
  • also 和 let 参数 block 是 「参数为 T 的函数」,所以采用 it 是唯一参数(argument)。另外因为 block 不是扩展函数,所以不存在 this 的定义。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容