kotlin扩展方法、属性

1.概念

​ kotlin支持在不修改类代码的情况下,动态为类添加属性(扩展属性)和方法(扩展方法)。

2.扩展方法

​ 扩展方法执行静态解析(编译时),成员方法执行动态解析(运行时)。

(1)语法格式

​ 定义一个函数,在被定义的函数前面添加“类名.”,该函数即为该类名对应类的拓展方法。

fun main(args: Array<String>) {
    val extensionClass = ExtensionClass()
    //调用拓展方法
    extensionClass.test()
}
//定义一个空类
class ExtensionClass
//为该空类定义一个拓展方法test()方法
fun ExtensionClass.test() = println("我是ExtensionClass的拓展方法")

(2)成员方法优先

​ 如果被扩展的类的扩展方法与该类的成员方法名字和参数一样,该类对象调用该方法时,调用的会是成员方法。

fun main(args: Array<String>) {
    val extension = ExtensionTest()
    //此处调用的会是成员方法
    extension.test()
}

class ExtensionTest {
    fun test() = print("成员方法")
}
//该方法不会被调用
fun ExtensionTest.test() = 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()
    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("子类扩展方法")

(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
    println(extensionTest.extensionParam)//a-b
}

/**
 * 定义一个类,包含属性param1、属性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"

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()//调用被扩展类的成员,相当于this.test()
        [email protected]()//同名的需要用this@类名的方式来调用
    }

    fun info(extensionTest: ExtensionTest) {
        extensionTest.func()
    }
}

5.带接收者的匿名扩展函数

(1)概念

​ 扩展方法(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 "我是来自带接收者的匿名扩展函数的返回值"
}

(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)
}

猜你喜欢

转载自www.cnblogs.com/nicolas2019/p/10932131.html