kotlin扩展⽅法、属性
1.概念
kotlin⽀持在不修改类代码的情况下,动态为类添加属性(扩展属性)和⽅法(扩展⽅法)。
2.扩展⽅法
扩展⽅法执⾏静态解析(编译时),成员⽅法执⾏动态解析(运⾏时)。
(1)语法格式
定义⼀个函数,在被定义的函数前⾯添加“类名.”,该函数即为该类名对应类的拓展⽅法。
fun main(args: Array<String>) {
val extensionClass = ExtensionClass()
//调⽤拓展⽅法
equals()方法
}
//定义⼀个空类
class ExtensionClass
//为该空类定义⼀个拓展⽅法test()⽅法
st() = println("我是ExtensionClass的拓展⽅法")
(2)成员⽅法优先
如果被扩展的类的扩展⽅法与该类的成员⽅法名字和参数⼀样,该类对象调⽤该⽅法时,调⽤的会是成员⽅法。
fun main(args: Array<String>) {
val extension = ExtensionTest()
//此处调⽤的会是成员⽅法
}
class ExtensionTest {
fun test() = print("成员⽅法")
}
//该⽅法不会被调⽤
st() = println("扩展⽅法")
(3)为系统类添加拓展⽅法(以String为例)
fun main(args: Array<String>) {
val str = "123456"
//调⽤String的拓展⽅法
println(str.lastIndex())
}
//为String定义⼀个拓展⽅法
fun String.lastIndex() = length - 1
(4)扩展实现原理
java是⼀门静态语⾔,⽆法动态的为类添加⽅法、属性,除⾮修改类的源码,并重新编译该类。
kotlin扩展属性、⽅法时看起来是为该类动态添加了成员,实际上并没有真正修改这个被扩展的类,kotlin实质是定义了⼀个函数,当被扩展的类的对象调⽤扩展⽅法时,kotlin会执⾏静态解析,将调⽤扩展函数静态解析为函数调⽤。
静态解析:根据调⽤对象、⽅法名到拓展函数,转换为函数调⽤。
如(2)str.lastIndex()⽅法执⾏的过程为:
①检查str类型(发现为String类型);
②检查String是否定义了lastIndex()成员⽅法,如果定义了,编译直接通过;
③如果String没定义lastIndex()⽅法,kotlin开始查程序是否有为String类扩展了lastIndex()⽅法(即是否有fun String.lastIndex()),如果有定义该扩展⽅法,会执⾏该扩展⽅法;
④既没定义lastIndex()成员⽅法也没定义扩展⽅法,编译⾃然不通过。
(5)静态解析调⽤扩展⽅法注意点
由于静态调⽤扩展⽅法是在编译时执⾏,因此,如果⽗类和⼦类都扩展了同名的⼀个扩展⽅法,引⽤类型均为⽗类的情况下,会调⽤⽗类的扩展⽅法。
/**
* 拓展属性、⽅法
*/
fun main(args: Array<String>) {
val father : ExtensionTest = ExtensionTest()
val child1 : ExtensionTest = ExtensionSubTest()
val child2 : ExtensionSubTest = ExtensionSubTest()
}
/**
* ⽗类
*/
open class ExtensionTest
/**
* ⼦类
*/
class ExtensionSubTest : ExtensionTest()
/**
* ⽗类扩展⼀个test⽅法
*/
st() = println("⽗类扩展⽅法")
/**
* ⼦类扩展⼀个test⽅法
*/
st() = println("⼦类扩展⽅法")
(6)可空类型扩展⽅法(以扩展equals⽅法为例)
kotlin允许扩展可空类型扩展⽅法,这样,null也能调⽤该⽅法。
fun main(args: Array<String>) {
val a: Any? = null
val b: Any? = null
println(a.equals(b))
}
fun Any?.equals(any: Any?): Boolean = this != null && any != null && any.equals(this)
3.扩展属性
(1)概念
kotlin允许动态为类扩展属性,扩展属性是通过添加get、set⽅法实现,没有幕后字段(filed)。
扩展属性也没有真的为该类添加了属性,只能说是为该类通过get、set⽅法计算出属性。
限制:①扩展属性不能有初始值;②扩展属性不能⽤filed关键字访问幕后字段;③val必须提供get⽅法,var必须提供get和set⽅法。(2)定义扩展属性
fun main(args: Array<String>) {
val extensionTest = ExtensionTest("a", "b")
println(extensionTest.param1)//a
println(extensionTest.param2)//b
sionParam)//a-b
}
/**
* 定义⼀个类,包含属性param1、属性param2
*/
class ExtensionTest(var param1: String, var param2: String)
/**
* 为该类扩展属性extensionParam
*/
sionParam: String
set(value) {
param1 = "param1$value"
param1 = "param2$value"
}
get() = "$param1-$param2"
4.以类成员⽅式定义扩展
在某个类⾥⾯为其他类定义扩展⽅法、属性,该扩展的⽅法,只能在该类中通过被扩展的类的对象调⽤扩展⽅法。
以类成员⽅式定义的扩展,属于被扩展的类,因此在扩展⽅法直接调⽤被扩展的类的成员(this可以省略),同时因为它位于所在类中,因此⼜可以直接调⽤所在类的成员。
fun main(args: Array<String>) {
val extensionTest = ExtensionTest()
val extensionTest2 = ExtensionTest2()
extensionTest2.info(extensionTest)
}
/**
* 定义⼀个类包含test⽅法
*/
class ExtensionTest {
fun test() = println("ExtensionTest的test⽅法")
}
/**
* 定义⼀个类包含test⽅法,包含ExtensionTest的⼀个扩展⽅法
*/
class ExtensionTest2 {
val a = "a"
fun test() = println("ExtensionTest2的test⽅法")
fun ExtensionTest.func() {
println(a)//调⽤扩展类的成员
test()//调⽤被扩展类的成员,相当于st()
st()//同名的需要⽤this@类名的⽅式来调⽤
}
fun info(extensionTest: ExtensionTest) {
extensionTest.func()
}
}
5.带接收者的匿名扩展函数
(1)概念
扩展⽅法(fun 类名.⽅法名())去掉⽅法名就是所谓的带接收者的匿名扩展函数,接收者就是类本⾝,形如:fun Int.() : Int。
fun main(args: Array<String>) {
val extensionTest = ExtensionTest()
NameExtensionFun("向带接收者的匿名函数传⼊的参数"))//使⽤匿名扩展函数
}
/**
* 定义⼀个空类
*/
class ExtensionTest
/**
* 为空类定义⼀个带接收者的匿名扩展函数
*/
var noNameExtensionFun = fun ExtensionTest.(param: String): String {
println(param)
return "我是来⾃带接收者的匿名扩展函数的返回值"
}
(2)带接收者的匿名扩展函数的函数类型
与普通函数⼀样,匿名扩展⽅法也有函数类型,(1)中的函数类型为:ExtensionTest.(String) -> String
(3)带接收者的匿名扩展函数与lambda表达式
如果能根据上下⽂推断出接收者类型,则可以使⽤lambda表达式
fun main(args: Array<String>) {
test {
println(it)
"匿名扩展函数返回值"
}
}
/
**
* 定义⼀个空类
*/
class ExtensionTest
/**
* 定义⼀个函数,形参为ExtensionTest.(String) -> String类型,相当于同时为ExtensionTest类扩展了⼀个匿名扩展函数
*/
fun test(fn: ExtensionTest.(String) -> String) {
val extensionTest = ExtensionTest()
println("调⽤匿名扩展函数:${extensionTest.fn("匿名扩展函数传⼊形参")}")
}
6.扩展使⽤场景
扩展极⼤的增加了程序的灵活性,java如果想对⼀个类扩展某些属性,必须通过继承的⽅式等实现,kotlin使⽤语法直接可以动态的扩展,能更⽅便组织⼀些⼯具⽅法等。
fun main(args: Array<String>) {
"打印⽇志".log()
}
/**
* 为String字符串添加⼀个打印⽇志的扩展⽅法
*/
fun String.log() {
println(this)
}