kotlin支持在不修改类代码的状况下,动态为类添加属性(扩展属性)和方法(扩展方法)。java
扩展方法执行静态解析(编译时),成员方法执行动态解析(运行时)。函数
定义一个函数,在被定义的函数前面添加“类名.”,该函数即为该类名对应类的拓展方法。工具
fun main(args: Array<String>) { val extensionClass = ExtensionClass() //调用拓展方法 extensionClass.test() } //定义一个空类 class ExtensionClass //为该空类定义一个拓展方法test()方法 fun ExtensionClass.test() = println("我是ExtensionClass的拓展方法")
若是被扩展的类的扩展方法与该类的成员方法名字和参数同样,该类对象调用该方法时,调用的会是成员方法。this
fun main(args: Array<String>) { val extension = ExtensionTest() //此处调用的会是成员方法 extension.test() } class ExtensionTest { fun test() = print("成员方法") } //该方法不会被调用 fun ExtensionTest.test() = println("扩展方法")
fun main(args: Array<String>) { val str = "123456" //调用String的拓展方法 println(str.lastIndex()) } //为String定义一个拓展方法 fun String.lastIndex() = length - 1
java是一门静态语言,没法动态的为类添加方法、属性,除非修改类的源码,并从新编译该类。日志
kotlin扩展属性、方法时看起来是为该类动态添加了成员,实际上并无真正修改这个被扩展的类,kotlin实质是定义了一个函数,当被扩展的类的对象调用扩展方法时,kotlin会执行静态解析,将调用扩展函数静态解析为函数调用。code
静态解析:根据调用对象、方法名找到拓展函数,转换为函数调用。对象
如(2)str.lastIndex()方法执行的过程为:
①检查str类型(发现为String类型);继承
②检查String是否认义了lastIndex()成员方法,若是定义了,编译直接经过;字符串
③若是String没定义lastIndex()方法,kotlin开始查找程序是否有为String类扩展了lastIndex()方法(便是否有fun String.lastIndex()),若是有定义该扩展方法,会执行该扩展方法;get
④既没定义lastIndex()成员方法也没定义扩展方法,编译天然不经过。
因为静态调用扩展方法是在编译时执行,所以,若是父类和子类都扩展了同名的一个扩展方法,引用类型均为父类的状况下,会调用父类的扩展方法。
/** * 拓展属性、方法 */ fun main(args: Array<String>) { val father : ExtensionTest = ExtensionTest() father.test()//调用父类扩展方法 val child1 : ExtensionTest = ExtensionSubTest() child1.test()//引用类型为父类类型,编译时静态调用的仍是父类的扩展方法 val child2 : ExtensionSubTest = ExtensionSubTest() child2.test()//此时才是调用子类的扩展方法 } /** * 父类 */ open class ExtensionTest /** * 子类 */ class ExtensionSubTest : ExtensionTest() /** * 父类扩展一个test方法 */ fun ExtensionTest.test() = println("父类扩展方法") /** * 子类扩展一个test方法 */ fun ExtensionSubTest.test() = println("子类扩展方法")
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)
kotlin容许动态为类扩展属性,扩展属性是经过添加get、set方法实现,没有幕后字段(filed)。
扩展属性也没有真的为该类添加了属性,只能说是为该类经过get、set方法计算出属性。
限制:①扩展属性不能有初始值;②扩展属性不能用filed关键字访问幕后字段;③val必须提供get方法,var必须提供get和set方法。
fun main(args: Array<String>) { val extensionTest = ExtensionTest("a", "b") println(extensionTest.param1)//a println(extensionTest.param2)//b println(extensionTest.extensionParam)//a-b } /** * 定义一个类,包含属性param一、属性param2 */ class ExtensionTest(var param1: String, var param2: String) /** * 为该类扩展属性extensionParam */ var ExtensionTest.extensionParam: String set(value) { param1 = "param1$value" param1 = "param2$value" } get() = "$param1-$param2"
在某个类里面为其余类定义扩展方法、属性,该扩展的方法,只能在该类中经过被扩展的类的对象调用扩展方法。
以类成员方式定义的扩展,属于被扩展的类,所以在扩展方法直接调用被扩展的类的成员(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()//调用被扩展类的成员,至关于this.test() this@ExtensionTest2.test()//同名的须要用this@类名的方式来调用 } fun info(extensionTest: ExtensionTest) { extensionTest.func() } }
扩展方法(fun 类名.方法名())去掉方法名就是所谓的带接收者的匿名扩展函数,接收者就是类自己,形如:fun Int.() : Int。
fun main(args: Array<String>) { val extensionTest = ExtensionTest() println(extensionTest.noNameExtensionFun("向带接收者的匿名函数传入的参数"))//使用匿名扩展函数 } /** * 定义一个空类 */ class ExtensionTest /** * 为空类定义一个带接收者的匿名扩展函数 */ var noNameExtensionFun = fun ExtensionTest.(param: String): String { println(param) return "我是来自带接收者的匿名扩展函数的返回值" }
与普通函数同样,匿名扩展方法也有函数类型,(1)中的函数类型为:ExtensionTest.(String) -> String
若是能根据上下文推断出接收者类型,则可使用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("匿名扩展函数传入形参")}") }
扩展极大的增长了程序的灵活性,java若是想对一个类扩展某些属性,必须经过继承的方式等实现,kotlin使用语法直接能够动态的扩展,能更方便组织一些工具方法等。
fun main(args: Array<String>) { "打印日志".log() } /** * 为String字符串添加一个打印日志的扩展方法 */ fun String.log() { println(this) }