前言
为了更动态的解决函数的扩展问题,进而提升编码效率。这算是一项程序员的福利。
除了文字版本,也有Xmind版本 github地址
目录结构是这样的
- 定义
- 扩展函数
- 扩展函数是静态解析的
- 可空接收者
- 扩展属性
- 伴生对象的扩展
- 扩展的作用域
- 扩展声明为成员
定义
Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。
比如说
- 为第三方库中无法修改的类编写 新function,新function就如同该类原来就存在的函数一般使用。
- 也可以为第三方类扩展一些新的属性
扩展函数
可以在已有类中添加新的方法,不会对原类做修改
fun receiverType.functionName(params){
body
}
receiverType
表示函数的接收者,也就是函数扩展的对象
functionName
扩展函数的名称
params
扩展函数的参数,可以为NULL
范例
class User(var name:String)
/**扩展函数**/
fun User.Print(){
print("用户名 $name")
}
fun main(arg:Array<String>){
var user = User("Runoob")
user.Print()
}
是不是有一种js的既视感
扩展函数是静态解析的
具体被调用的的是哪一个函数
由调用函数的的对象表达式来决定
open class C
class D: C()
fun C.foo() = "c" // 扩展函数 foo
fun D.foo() = "d" // 扩展函数 foo
fun printFoo(c: C) {
println(c.foo()) // 类型是 C 类
}
fun main(arg:Array<String>){
printFoo(D()) //#输出c
}
若扩展函数和成员函数一致
优先使用成员函数
class C {
fun foo() { println("成员函数") }
}
fun C.foo() { println("扩展函数") }
fun main(arg:Array<String>){
var c = C()
c.foo() //#输出 成员函数
}
可空接收者
范例
fun Any?.toMyString(): String {
if (this == null) return "null"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}
这里的Any?是一个可以为null的对象;
通过 this == null的判断,可以让你在没有进行null判断,就调用toMyString方法
扩展属性
val <T> List<T>.lastIndex: Int
get() = size - 1
lastIndex是扩展出来的属性
只能通过get来获取
扩展属性只能被声明为 val
允许定义在类或者kotlin文件中
不允许定义在函数中
否则会报Local extension properties are not allowed
范例
class Outer2 { // 外部类
private val bar: Int = 1
}
val Outer2.newBar : Int
get() = 222212
fun main(args: Array<String>) {
println(Outer2().newBar) // 222212
}
因为是扩展的属性,他是不具备初始化器的。
- 扩展不能真正的修改他们所扩展的类
- 你并没有在一个类中插入新成员
- 仅仅是可以通过该类型的变量用点表达式去调用这个新函数
范例二
val House.number = 1 // 错误:扩展属性不能有初始化器
伴生对象的扩展
class MyClass {
companion object { } // 将被称为 "Companion"
}
companion object即伴生对象
伴生对象在类中只能存在一个
类似于java中的静态方法
这可以这么写。
范例一
class MyClass {
companion object CcName{
} // 将被称为 "Companion"
}
fun MyClass.CcName.printCompanion() { println("companion") }
fun main() {
MyClass.CcName.printCompanion()
}
范例二
class MyClass {
companion object { } // 将被称为 "Companion"
}
fun MyClass.Companion.printCompanion() { println("companion") }
fun main() {
MyClass.printCompanion()
}
扩展的作用域
与java一样
需要import具体的,或者*
大多数时候我们在顶层定义扩展
- 直接在包里
- 扩展范例
package org.example.declarations fun List<String>.getLongestString() { /*……*/}
使用所定义包之外的一个扩展
调用范例
package org.example.usage
import org.example.declarations.getLongestString
fun main() {
val list = listOf("red", "green", "blue")
list.getLongestString()
}
扩展声明为成员
在一个类内部你可以为另一个类声明扩展
范例
class Host(val hostname: String) {
fun printHostname() { print(hostname) }
}
class Connection(val host: Host, val port: Int) {
fun printPort() { print(port) }
fun Host.printConnectionString(p: Int) { //这是扩展函数。函数内的this指向Host本身。
//但是可以通过this指定到Connection的this
printHostname() // calls Host.printHostname()
print(":")
printPort() // calls Connection.printPort()
}
fun connect() {
/*……*/
host.printConnectionString(port) // calls the extension function
}
}
fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString(443) // error, the extension function is unavailable outside Connection
}
这个范例,kotlincn的说法其实是有点绕口的。细细品味,文中所谓的,不正说的是 this的指向问题吗?