我的Kotlin学习之旅(六)

扩展
初次见到扩展的概念的时候,确实是有点不太理解的,完全搞不懂这是个什么东东。
翻译文档上是这么说的:
与 C# 和 Gosu 类似, Kotlin 也提供了一种,可以在不继承父类,也不使用类似装饰器这样的设计模式的情况下对指定类进行扩展。我们可以通过一种叫做扩展的特殊声明来实现他。Kotlin 支持函数扩展和属性扩展。
好吧,没怎么看懂,就只能通过具体的例子来理解它了。

这里试一下函数扩展,为了声明一个函数扩展,我们需要在函数名前面添加一个接受者类型来作为前缀(意思是这种类型的对象可以调用该扩展函数),接下来是具体的例子:

 fun ArrayList.swap(x: Int,y :Int){
        val temp = this[y]
        this[y] = this[x]
        this[x] = temp
    }

我在这里定义了一个swap函数,作用是交换类型是ArrayList的对象的指定的两个位置的值,同时这个ArrayList对象的值得类型是Int(这里只是为了举例子方法,其实也可以定义成泛型T)。

然后看一下该函数的使用:

class MainActivity : AppCompatActivity() {

        private var mShow: TextView? = null

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)

            mShow = findViewById(R.id.show) as TextView?

            val list: ArrayList = ArrayList()
            list.add(111)
            list.add(222)
            list.add(333)

            list.swap(0,1)
            var str = ""
            for (i in 0 until list.size) {
                str += (""+list[i] + ";")
            }

            mShow!!.text = str

        }

    }

我在这里声明了一个类型为ArrayList的list,它的长度是3,0,1,2三个位置上的值分别是 111,222,333
调用list.swap(0,1)交换了0和1位置上的数值,应该是变成 222,111,333
通过xml中的TextView,将数据展示出来,最后显示的结果就是222,111,333
说明这样做是可行的。

至此,总结一下函数扩展:定义了类似于上述的扩展函数之后,函数的类型接受者就可以直接通过实例来调用该函数了。

最后再看一下文档,深入了解一下扩展的概念:
扩展实际上并没有修改它所扩展的类。定义一个扩展,你并没有在类中插入一个新的成员,只是让这个类的实例对象能够通过.调用新的函数。

再来看一个很重要的概念:扩展是被静态解析的

open class C 

class D: C()

fun C.foo() = "c" 

fun D.foo() = "d"

fun printFoo(c: C) { 
    println(c.foo())
} 

printFoo(D())

用文档里的代码来解释吧,这里有一个可被继承的类C,D继承自C,然后类C有一个扩展函数foo(),它打印的值是“c”,而它的继承者D,也有一个相同名字的扩展函数,打印的值是”d”。最后有一个printFoo(c: C),重点在这里,
它这里接收一个形参,C。由于D继承自C,所以也可以被接收。

那么最后调用printFoo函数,输入D对象,输出的到底是”c”还是”d”呢?
这就用到了这句话:扩展是被静态解析的。这里的扩展函数的调用的决定这在于printFoo函数的那个形参的类型,也就是这里的决定者是C,而不是实际传入的参数。所以只要记住这句话:扩展函数的调用决定于是声明的类型,而不是实际传入的类型

再来看一个优先级问题:如果一个类中,有一个和该类的扩展函数同名同参数的成员函数,那么当该类的对象在调用该名称的方法时,会调用哪个呢?
答案:如果有同名同参数的成员函数和扩展函数,调用的时候必然会使用成员函数
也就是说,成员函数的优先级要比扩展函数的优先级要高

可空的接收者
扩展可以使用空接收者类型进行定义。这样的扩展使得,即使是一个空对象仍然可以调用该扩展,然后在扩展的内部进行 this == null 的判断。这样你就可以在 Kotlin 中任意调用 toString() 方法而不进行空指针检查:空指针检查延后到扩展函数中完成

fun Any?.toStrings(): String {
    if (this == null) return "null"
    // 在空检查之后,`this` 被自动转为非空类型,因此 toString() 可以被解析到任何类的成员函数中
    return toString()
}

属性扩展

直接看例子:

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

使得类型是ArrayList的对象的名为lastIndex的扩展属性的值为 list的长度 - 1,即返回的是list的最后一个值得下标
亲测有用哟
需要注意的点:
扩展并不会真正给类添加了成员属性
扩展属性只能够通过明确提供 getter 和 setter方法来进行定义.

你可能感兴趣的:(Kotlin学习之旅)