Kotlin-12.扩展函数和属性(extensions)

官方文档: http://kotlinlang.org/docs/reference/extensions.html

1.扩展(extensions)

在不修改原类的情况下,
Kotlin能给一个类扩展新功能,无需继承该类,也不用任何设计模式(如装饰模式等),
Kotlin支持扩展函数和扩展属性!

为什么要使用扩展(动机):
    在Java中,有很多工具类如java.util.Collections,使用很繁琐:
        // Java
        Collections.swap(list, Collections.binarySearch(list, 
                Collections.max(otherList)), Collections.max(list))

    静态导入Collections类,简化写法:
        // Java
        swap(list, binarySearch(list, max(otherList)), max(list))

    静态导入使用依然很麻烦,如果能给list类添加扩展函数就好了:      
        list.swap(list.binarySearch(otherList.max()), list.max())

2.类-扩展函数

1.定义

为MutableList类扩展一个swap函数:
    fun MutableList<Int>.swap(index1: Int, index2: Int) {
        val tmp = this[index1]  //this: 当前MutableList对象 
        this[index1] = this[index2]
        this[index2] = tmp
    }    
对MutableList对象调用swap函数:
    val list = mutableListOf(1, 2, 3)
    list.swap(0, 2)

MutableList泛化类型:
    //为在表达式中使用泛型,要在函数名前添加泛型参数!
    fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
        val tmp = this[index1]
        this[index1] = this[index2]
        this[index2] = tmp
    }

2.静态解析(没有多态)

扩展不能真正修改类,即没有在一个类中插入新成员!
扩展函数是静态解析分发的,不是虚函数(即没有多态),调用只取决于对象的声明类型!
1.调用是由对象声明类型决定,而不是由对象实际类型决定!
    open class C
    class D: C()
    fun C.foo() = "c"
    fun D.foo() = "d"
    fun printFoo(c: C) {
        println(c.foo()) //扩展函数是静态解析的,不是虚函数(即没有多态)
    }
    fun main(args: Array<String>) {
        printFoo(D()) //输出"c",扩展函数调用只取决于参数c的声明类型 
    }

2.类的成员函数和扩展函数-同名同参数:
    class C {
        fun foo() { println("member") }
    }
    fun C.foo() {
        println("extension") 
    }
    fun main(args: Array<String>) {
        val c = C()
        println(c.foo()) //输出“member”
    }

3.类的成员函数和扩展函数-同名不同参数:
    class C {
        fun foo() { println("member") }
    }
    fun C.foo(i: Int) {
        println("extension")
    }
    fun main(args: Array<String>) {
        val c = C()
        println(c.foo(2)) //输出"extension"
    }

3.可空接收者

可null的类型定义扩展,即使对象为null,也可在对象上调用!
    fun Any?.toString(): String {
        if (this == null) return "null"
        return toString()
    }

2.类-扩展属性

和扩展函数类似,Kotlin也支持扩展属性:
    val <T> List<T>.lastIndex: Int // 不能初始化
        get() = size - 1 // 只能由getters/setters显式提供

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

由于扩展没有在类中插入新成员,因此扩展属性无法使用幕后字段,
这就是为什么扩展属性不能有初始化器,只能由getters/setters显式提供!

3.伴生对象-扩展函数和属性

可为伴生对象定义扩展函数和属性:
    class MyClass {
        companion object { }  //伴生对象
    }

    fun MyClass.Companion.foo() {
        // ……
    }

    MyClass.foo() //用类名调用

4.作用域

1.扩展直接在包中

在顶层定义扩展(即直接在包中):
    package foo.bar
    fun Baz.goo() {
        ...            
    }

在其它包调用:
    package com.example.usage
    import foo.bar.goo //导入所有名为“goo”的扩展
    // 或者 import foo.bar.*
    fun usage(baz: Baz) {
        baz.goo()
    }

2.扩展作为类成员

在一个类内部可为另一个类声明扩展,   
扩展声明所在的类称为分发接收者(dispatch receiver),
扩展函数调用所在类称为扩展接收者(extension receiver)

1.定义
    class D { //扩展接收者(extension receiver)
        fun f() { …… }
    }

    class C { //分发接收者(dispatch receiver)
        fun f() { …… }

        fun D.foo() {
           this@C.f() //分发接收者 C.f()
           f()        //扩展接收者 D.f()
        }

        fun call(d: D) {
            d.foo()   //调用扩展函数
        }
    }

2.继承-覆盖
成员扩展可声明为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 call(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")
        }
    }
    C().call(D())   // 输出 "D.foo in C"
    C().call(D1())  // 输出 "D.foo in C", 扩展接收者静态解析(非多态)
    C1().call(D())  // 输出 "D.foo in C1",分发接收者虚拟解析(多态)

简书:http://www.jianshu.com/p/b848b62e35ef
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/73556622
GitHub博客:http://lioil.win/2017/06/21/Kotlin-extensions.html
Coding博客:http://c.lioil.win/2017/06/21/Kotlin-extensions.html

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

推荐阅读更多精彩内容