Skip to content

Latest commit

 

History

History
145 lines (98 loc) · 4.33 KB

Extensions.md

File metadata and controls

145 lines (98 loc) · 4.33 KB

##扩展

与 C# 和 Gosu 类似, Kotlin 也提供了一种渠道,可以在不继承父类,也不使用类似装饰器这样的设计模式的情况下对指定类进行扩展。我们可以通过一种叫做扩展的特殊声明来实现他。现在, Kotlin 支持扩展函数和扩展属性。

###扩展函数

为了声明一个扩展函数,我们需要在函数名使用接收者类型作为前缀。下面我们会为 MutableList<Int> 添加一个 swap 函数:

fun MutableList<Int>.swap(x: Int, y: Int) {
	val temp = this[x] // this 对应 list
	this[x] = this[y]
	this[y] = tmp
}

在扩展函数中的 this 关键字对应接收者对象。现在我们可以在任何 MutableList<Int> 实例中使用这个函数了:

val l = mutableListOf(1, 2, 3)
l.swap(0, 2)

当然,这个函数对任意的 MutableList<T> 都是适用的,而且我们可以把它变的通用:

fun <T> MutableList<T>.swap(x: Int, y: Int) {
  val tmp = this[x] // 'this' corresponds to the list
  this[x] = this[y]
  this[y] = tmp
}

我们在函数名前声明了通用类型,从而使他可以接受任何参数。参看泛型函数

###扩展是静态解析的

扩展实际上并没有修改它所扩展的类。定义一个扩展,你并没有在类中插入一个新的成员,只是让这个类的实例对象能够通过.调用新的函数。

需要强调的是扩展函数是静态分发的,举个例子,它们并不是接受者类型的虚拟方法。如果有同名同参数的成员函数和扩展函数,调用的时候必然会使用成员函数,比如:

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

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

当我们对C的实例c调用c.foo()的时候,他会输出"member",而不是"extension"

###空接受者

注意扩展可以使用空接受者进行定义。那样的话,扩展可以在一个值为空的对象变量被调用,并在函数体内检查 this == null 。这样你就可以在 Kotlin 中任意调用 toString() 方法而不进行空指针检查:空指针检查延后到扩展函数中完成。

fun Any?.toString(): String {
	if (this == null) return "null"
	return toString()
}

###扩展属性

和函数类似, Kotlin 也支持属性扩展:

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

注意,由于扩展并不会真正给类添加了成员属性,因此也没有办法让扩展属性拥有一个备份字段.这也是为什么初始化函数是不允许扩展属性。扩展属性只能够通过直接提供 getter 和 setter方法来进行定义.

例子:

val Foo.bar = 1 //error: initializers are not allowed for extension properties

###伴随对象扩展

如果一个对象定义了伴随对象,你也可以给伴随对象添加扩展函数或扩展属性:

class MyClass {
	companion object {} 
}
fun MyClass.Companion.foo(){

}

和普通伴随对象的成员一样,它们可以只用类的名字就调用:

MyClass.foo()

###扩展的域

大多数时候我们在 top level 定义扩展,就在包下面直接定义:

package foo.bar
fun Baz.goo() { ... }

为了在除声明的包外使用这个扩展,我们需要在 import 时导入:

package com.example,usage

import foo.bar.goo // 导入所有名字叫 "goo" 的扩展
				
					// 或者

import foo.bar.* // 导入foo.bar包下得所有数据

fun usage(baz: Baz) {
	baz.goo()
}

###动机

在 java 中,我们通常使用一系列名字为 "*Utils" 的类: FileUtils,StringUtils等等。很有名的 java.util.Collections 也是其中一员的,但我们不得不像下面这样使用他们:

//java
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))

由于这些类名总是不变的。我们可以使用静态导入并这样使用:

swap(list, binarySearch(list, max(otherList)), max(list))

这样就好很多了,但这样我们就只能从 IDE 自动完成代码那里获得很少或得不到帮助信息。如果我们可以像下面这样那么就好多了

list.swap(list.binarySearch(otherList.max()), list.max())

但我们又不想在 List 类中实现所有可能的方法。这就是扩展带来的好处。