Kotlin学习之扩展函数

作者: 程序员丶星霖 | 来源:发表于2017-12-12 08:55 被阅读145次

    Kotlin学习之扩展函数

    扩展函数是指在一个类上增加一种新的行为,甚至没有这个类代码的访问权限。

    一、扩展函数

    在Java中,通常会实现很多带有static方法的工具类。Kotlin中扩展函数的一个优势就是不需要在调用方法的时候把整个对象当作参数传入
    扩展函数表现的就像是属于这个类的一样,可以使用this关键字调用所有public方法。

    例如:
    创建一个toast函数,不需要传入任何context,它可以被任何Context或者其子类所调用。

    fun Context.toast(message:CharSequence,duration: Int=Toast.LENGTH_SHORT){
        Toast.makeText(this,message,duration).show()
    }
    
    • 这里的this关键字在扩展函数内部对应到接受者对象

    上面的方法在Activity中可以直接调用:

    toast("Hello world!")
    toast("Hello world!", Toast.LENGTH_LONG)
    
    • 扩展函数也可以是一个属性。
    • 扩展函数并不是真正地修改了原来的类,只是以静态导入的方式来实现的。
    • 扩展函数可以被声明在任意文件中,所以可以把一系列有关的函数放在一个新建的文件里。

    二、扩展是静态解析的

    扩展并不能真正的修改其所扩展的类。通过定义一个扩展,并没有在一个类中插入新的成员,只是可以通过该类型的变量用点(.)表达式去调用这个新函数。

    扩展函数不是静态分发的,是由函数调用所在的表达式的类型来决定的,而不是由表达式运行时求值结果决定的。

    例如:

    open class C
    
    class D: C()
    
    fun C.foo() = "c"
    
    fun D.foo() = "d"
    
    fun printFoo(c: C) {
        println(c.foo())
    }
    
    printFoo(D())
    

    上例中会输出“c”,因为调用的扩展函数只取决于参数c的声明类型。

    如果一个类定义有一个成员函数和一个扩展函数,而这两个函数又有相同的接收类型、相同的名字并且都适用给定的参数,这种情况总是取成员函数。

    例如:

    class C {
        fun foo() { println("member") }
    }
    
    fun C.foo() { println("extension") }
    

    如果此时调用C类型c的c.foo(),将会输出"member"

    例如:

    class C {
        fun foo() { println("member") }
    }
    
    fun C.foo(i: Int) { println("extension") }
    

    调用C().foo(1)将输出"extension"。

    三、可空接收者

    可以为可空的接收者类型定义扩展。

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

    四、扩展属性

    Kotlin也支持扩展属性:

    val <T> List<T>.lastIndex: Int
        get() = size - 1
    

    注意:

    • 由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。
    • 扩展属性不能有初始化器,只能显式提供的getters/setters定义。

    五、伴生对象的扩展

    如果一个类有一个伴生对象,可以为伴生对象定义扩展函数和属性:

    class MyClass {
        companion object { }  // 将被称为 "Companion"
    }
    
    fun MyClass.Companion.foo() {
        // ……
    }
    

    六、扩展声明为成员

    在一个类内部可以为另一个类声明扩展。在这样的扩展内部,有多个隐式接收者——其中的对象成员可以无需通过限定符访问。

    扩展声明所在类的实例称为分发接收者,扩展方法调用所在的接收者类型的实例称为扩展接收者

    class D {
        fun bar() { …… }
    }
    
    class C {
        fun baz() { …… }
    
        fun D.foo() {
            bar()   // 调用 D.bar
            baz()   // 调用 C.baz
        }
    
        fun caller(d: D) {
            d.foo()   // 调用扩展函数
        }
    }
    

    对于分发接收者和扩展接收者的成员名字冲突的情况,扩展接收者优先。要引用分发接收者的成员你可以使用 限定的 this 语法。

    class C {
        fun D.foo() {
            toString()         // 调用 D.toString()
            this@C.toString()  // 调用 C.toString()
        }
    

    声明为成员的扩展可以声明为 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()   // 调用扩展函数
        }
    }
    
    class C1 : C() {
        override fun D.foo() {
            println("D.foo in C1")
        }
    
        override fun D1.foo() {
            println("D1.foo in C1")
        }
    }
    
    C().caller(D())   // 输出 "D.foo in C"
    C1().caller(D())  // 输出 "D.foo in C1" —— 分发接收者虚拟解析
    C().caller(D1())  // 输出 "D.foo in C" —— 扩展接收者静态解析
    

    七、with函数

    with是一个非常有用的函数,它包含在Kotlin的标准库中。它接收一个对象和一个扩展函数作为其参数,然后使这个对象扩展这个函数。
    表示所有在括号中编写的代码都是作为对象的一个扩展函数,可以像作为this一样来使用其public方法和属性。

    学海无涯苦作舟

    我的微信公众号.jpg

    相关文章

      网友评论

        本文标题:Kotlin学习之扩展函数

        本文链接:https://www.haomeiwen.com/subject/nddgixtx.html