扩展
kotlin和C#相似,提供了扩展类的能力,通过使新的函数,而不是通过继承或者装饰这几模式。kotlin支持extentions方法和extentions属性。
扩展方法
为了声明一个扩展方法,我们需要一个接收类型的样板。下面增加了一耳光swap方法到MutableList :
fun MutableList<Int>.swap(index1 : Int , index2 : Int){
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
关键字this在一个扩展函数中,关联接收类型。现在我们可以在MutableList调用这样一个方法
val l = muitableListOf(1,2,3)
l.swap(0,2)
当人,这个方法可以扩展到泛型
fun <T> MutableList<T>.swap(index1 : Int , index2 : Int){
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
我们声明泛型参数在方法名之前,并且在接收类型扩展时,可以使用
扩展内容被静态解析
扩展实际上不是类的扩展。通过定义一个扩展,并不是往类中插入成员,而是使得带“.”方式进行调用的韩式。
我们逍遥敲打扩展方法时是静态的,这就意味着要被调用的扩展方法是由表达式类型那个决定的,而不是结果的类型决定的。
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,它是C class
如果一个类有一个成员函数,并且扩展函数也被定义为相同的接收类型那个,这是优先调用成员
class C{
fun foo(){
print("memeber")
}
}
fun C.foo(){
println("extention")
}
结果是 “memeber”
如果两者签名不同,将按着方法签名调用
class C {
fun f(){
print("memeber")
}
}
fun C.f(a : Int){
print("extention")
}
可空的接收者
注意 扩展可以被定义为 一个可空的接收类型那个。这样的扩展,可以被一个空对象条用,能够在方法体内检查对象是否非空。
fun Any?.toString():String{
if(this == null) return "null"
return toString()
}
扩展属性
和方法相似,属性也支持扩展
val <T> List<T>.lastIndex : Index
get() = size -1
注意,扩展的本质并不是将成员插入到类中,因此没有一个有效的方式扩展属性,达到“backing field”的效果。这就是为什么初始化器不能初始化扩展属性。它们只能定义getter和setter方法。
val Foo.bar = 1 //这是错误的
友元对象的扩展
如果一个类已经顶一个友元对象,你可以针对它进行扩展。
class MyClass{
companion object {}
}
fun MyClass.Companion.foo(){
}
并且像componion oject的普通成员一样,它们可以通过类名进行调用
MyClass.foo()
扩展范围
通常我们是包下,进行顶级扩展
package foo.bar
fun Baz.goo(){}
为了在包外使用,我们需要进行导包。
package com.example.useage
import foo.bar.goo
import foo.bar.* //包下所有内容
fun usage(baz : Baz){
baz.goo()
}
声明扩展作为成员
在一个类中,你可以为另一个声明扩展。在这样的一个扩展的内部,有许多隐式的不用标识符的直接访问的接收对象成员。扩展所在类的实例被调用。
class D{
fun bar(){}
}
class C{
fun baz(){}
fun D.foo(){
bar()
baz()
}
fun caller(d :D){
d.foo()
}
}
有一种情况–分发接收者类内部的成员与扩展接收者中是冲突,则需要进行如下指明:
class C{
fun D.foo(){
toString()
this@C.toString()
}
}
如果成员扩展被声明为open,则可以被继承。在这里要注意的是 分发接收者是可以支持多态的,但是扩展接收者是 静态的
open class D{
}
class D1 : D(){
}
open class C{
open fun D.foo(){
print("D.foo in C")
}
open fun D1.foo(){
print("D1.foo in C")
}
fun caller(d:D){
d.foo()
}
}
class C1 :C(){
open fun D.foo(){
print("D.foo in C1")
}
open fun D1.foo(){
print("D1.foo in C1")
}
}
C().caller(D())
C1().caller(D()) //分发 使用的是多态
C().caller(D1())//扩展使用的是静态
动机
在java中,我们习惯命名各种utls类。最著名的是java.util.Collections.但是它们的使用却是如下格式:
//java
Colletions.swap(list, Collections.binarySearch(list, Collections.max(otherList),Collections.max(list));
这些类的名字,可以同过静态导入:
//java
swap(list, binarySearch(list,max(otherList)),max(list));
在高度优秀IDE自动面前,这样的有优化显然是微弱的。理想格式如下:
list.swap(list.binarySearch(otherList.max()),list.max());
但是我们不想在List中实现所有的扩展方法,这就是 扩展可以帮我们做的事情。