一篇小文理解Kotlin之扩展函数/属性
从一次尴尬的经历说起~~
话说
long long ago
,接手的项目代码都是经过N
手蹂躏后的,于是乎漫长而逐步的重构是必不可少的。近两年多都是Kotlin
语言主力开发的习惯下,我似乎不会写Java
了~。
public void test(){
StringBuilder sb = new StringBuilder();
//业务1
sb.appendln("abc");
println(sb.toString());
//业务2
sb = new StringBuilder();
sb.appendln("defg");
println(sb.toString());
}
卧槽~~,虽然说StringBuilder
对比不停的new String
或者拼接字符串来说,效率和性能都高很多。但是这里多次频繁的new StringBuilder()
显然也是冗余的,不合理的。
二话不说,重构逻辑,于是乎懵逼了~~
fun test(){
val sb = StringBuilder()
sb.clear()//可以清空sb,用于下次applend
}
而Java
的API
中,并没有StringBuidler.clear
函数,有delete
,虽然也能实现需要的功能。然而,就然而了。
Kotlin扩展
-
扩展函数
- 通过
ExtClass.xxx()
的形式类定义扩展函数,其中ExtClass
就称为接收者(Receiver)
//给String添加name的扩展函数 fun String.name():String{ //在扩展函数内,可以this代指被扩展类对象 return "Name: length = ${this.length()}" }
- 扩展是静态解析的
//定义类,一个开放类和其继承子类 open class Shape class Rectangle:Shape() //扩展 fun Shape.getName() = "Shape" fun Rectangle.getName() = "Rectangle" //定义一个函数,接收泛型参数 fun printName(s:Shape){ //注意,这里形参是Shape,而非Rectangle println(s.getName()) } //测试 fun testDemo(){ //调用函数的形参是,Shape 所以即使传入的是Rectangle的实际类型,起作用的仍然是Shape的扩展。 printName(Rectangle()) //其输出结果是 “Shape”,而不是“Rectangle” }
扩展函数是静态解析的,取决于声明的类型,而不是实际运行时的类型。
- 如果扩展函数与原有内部函数重名,实际调用起作用的是原生内部的。亲生的更重要呀
class AA{ fun printA(){ println("亲生的AA打印") } } //扩展定义 fun AA.printA(){ println("收养的AA的打印") } fun AA.printA(i:Int){ println("虽然是收养的,但是是特别的") } //测试demo fun testDemo(){ AA().printA()//输出的是亲生的啊 AA().printA(99)//输出的就是特别的,这里就不会过分到袒护亲生而厚此薄彼了。 }
注:重名是指函数名+函数签名都一致。如果签名不一致,也就是方法重载了,那么就不算厚此薄彼。
- 可空
receiver
//可以对nullable的扩展 Any?.toString():String{ return if(this==null)"null" else this.toString() }
- 通过
-
扩展属性
-
类似于扩展函数,可以对
receiver
扩展属性//对String扩展name属性, val String.name ="name"//这么些就错了,不能初始化 //可以有getter和setter(val 的不能有setter) val String.name get()= "name" var String.name set(value){name="name"} get()="name"
注:扩展属性没有
back field
,所以不能有初始化。
对于伴生对象
Companion object
同样可以进行扩展函数/属性 -
-
扩展的作用域
一般在
kt
文件的top level
直接定义扩展函数,其作用域是根据kotlin中权限修饰符控制。调用方直接导入即可。
-
扩展声明成员
就是在一个类中定义另一个类的扩展函数。
class A{ //... } class B{ //定义A的扩展 fun A.name(){ } //B的普通函数 fun ppB(){ //在B内可以调用A的扩展函数,B之外则无效。 A().name() } } //测试 fun testDemo(){ A().name()//不行的,调不到 B().name()//更扯淡,根本就不是B的函数 B().ppB() }
这里
B
称为分发接收者dispatcher receiver ,而A
称为扩展分发者extends receiver//如果调用函数重名,扩展分发者的优先,或者需要特别指定 class AA{} class BB{ //扩展的AA的info fun AA.info(){} //BB自己的info fun call(){} //AA的扩展函数 fun AA.pp(){ //如果调用info info()//默认是AA自己的info this@BB.info()//可以指定BB的info } }
-
扩展函数可以open
即,扩展函数也可以被重写
open class Base { } class Derived : Base() { } open class BaseCaller { open fun Base.printFunctionInfo() { println("----Base 在 BaseCaller 中 -----") } open fun Derived.printFunctionInfo() { println("----Derived 在 BaseCaller 中------") } fun call(b: Base) { b.printFunctionInfo() // 调用扩展函数 } } class DerivedCaller: BaseCaller() { override fun Base.printFunctionInfo() { println("Base 在 DerivedCaller 中") } override fun Derived.printFunctionInfo() { println("Derived 在 DerivedCaller 中") } } @Test fun testExt() { BaseCaller().call(Base()) // “Base extension function in BaseCaller” BaseCaller().call(Derived()) DerivedCaller().call(Base()) // “Base extension function in DerivedCaller”——分发接收者虚拟解析 DerivedCaller().call(Derived()) // “Base extension function in DerivedCaller”——扩展接收者静态解析 }
输出结果
----Base 在 BaseCaller 中 ----- ----Base 在 BaseCaller 中 ----- Base 在 DerivedCaller 中 Base 在 DerivedCaller 中
由扩展函数是静态解析的,可知,call函数形参是Base,所以不论入参是Base()还是Derived()都只会调用成Base的扩展的call。
而不同的分发接收者调用,则会执行不同的扩展函数的具体。