• Kotlin学习快速入门(7)——扩展的妙用


    本文为作者原创,转载请注明出处,谢谢配合
    作者:stars-one
    链接:https://www.cnblogs.com/stars-one/p/16440099.html

    本篇大约有4105个字,阅读预计需要5.13分钟


    原文地址: Kotlin学习快速入门(7)——扩展的妙用 - Stars-One的杂货小窝

    之前也模模糊糊地在用这个功能,也是十分方便,可以不用继承,快速给某个类增加新的方法,本篇便是来讲解下Kotlin中扩展这一概念的使用

    说明

    先解释一下,扩展的说明,官方文档上解释:

    Kotlin 能够扩展一个类的新功能,而无需继承该类或者使用像装饰者这样的设计模式

    简单来说,就是可以不用继承来让一个类多出一个方法或属性(成员变量),可能这样说比较抽象,我们以一个简单的例子来说

    比如说,我们需要用到以下功能:

    判断String对象是否其是否为null或未空白字符串,如果为null或空白字符串,则返回true,否则返回false

    此功能挺好实现,但我们想要实现此功能,无非就是3种方法:

    1. 写个工具类StringUtil,然后传递有个String对象进去,方法返回
    2. 写个新的类,让其继承于String类,之后再新增方法
    3. 用装饰者模式,扩展类(这里不多解释装饰者模式,可以自己百度查阅下资料)

    但上面的方法,估计第一种各位都明白,也是十分简单,但使用起来还是比较麻烦,还得将对象作为入参传递,如果使用Kotlin的扩展特性,还能变得更加简单

    而剩下两种,改动均是较大,一般得看情况使用,也是不太推荐

    扩展方法

    我们以刚才上述说的功能为例,实现判断String对象是否其是否为null或未空白字符串,如果为null或空白字符串,则返回true,否则返回false此功能

    语法及使用

    首先,显示讲解下语法

    fun 类名.方法名(参数列表...):返回值{
        
    }
    

    看起来稍微有些抽象,我们直接上示例:

    fun String.isBlankOrNullString(): Boolean {
        return this == null || this.trim().length == 0
    }
    

    需要注意的是,方法里的this就是当前调用此方法的String对象

    扩展方法使用:

    fun main(args: Array<String>) {
        val str = ""
        println(str.isBlankOrNullString())
    }
    

    PS: 这里的扩展方法写在了顶层,是全局可用的

    注意点

    1. 扩展方法会区分作用域(全局和局部)
    2. 类中存在于扩展方法同名同参数列表,相当于重载,此时以扩展方法为主
    3. 扩展方法可接收可空类型

    扩展方法作用域

    扩展方法的声明位置,会决定此扩展方法的作用域

    如下面示例:

    fun main() {
        val str = ""
        println(str.isBlankOrNullString())
    }
    
    class User {
        val str = ""
    }
    
    fun String.isBlankOrNullString(): Boolean {
        return this == null || this.trim().length == 0
    }
    

    我们将方法写在了最外层(即与class关键字同级),此时,我们可以在任意的类中调用此方法

    但如果我们稍微改一下,如下:

    fun main() {
        val str = ""
        //这里会报错!!
        //println(str.isBlankOrNullString())
    }
    
    class User {
        val str = ""
    
        fun sayHello() {
            //类中可以正常使用
            str.isBlankOrNullString()
        }
    
        fun String.isBlankOrNullString(): Boolean {
            return this == null || this.trim().length == 0
        }
    
    }
    

    扩展方法重载问题

    由于是声明方法,可能会出现方法名重名的情况,即我们Java基础中说到的重载关系

    这里,如果出现了重载的情况(方法名和参数列表相同),会以类中的方法为主(即会忽略掉扩展方法)

    上面此句,是根据文档上总结得来的,实际上也是测试通过

    fun main() {
        Example().printFunctionType()
    }
    
    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType() { println("Extension function") }
    

    最后输出的是Class method

    但这里有个奇怪的情况,我以String的扩展为例,测试发现与上述结论不一致!!

    以下是我的测试代码:

    fun main() {
        val str = ""
        println(str.isNullOrBlank())
    }
    
    fun String.isNullOrBlank(): Boolean {
        println("进入我们的方法里")
        return this == null || this.trim().length == 0
    }
    

    最终输出:

    进入我们的方法里
    true
    

    看着打印,这明显就是进到我们定义的扩展方法里啊 😕

    研究了一番,发现原本的那个isNullOrBlank(),并不是String类中含有的方法

    官方也是通过扩展方法来实现追加的,且是扩展的类是CharSequence,而且此类是个接口类,所有实现了此接口的类都有了isNullOrBlank()方法

    而我们自己也是定义了个扩展方法,与官方的扩展方法发生了重载,于是我们的扩展方法便是把官方的扩展方法覆盖了

    所以得出以下结论:

    当类中存在某个方法,扩展方法与此方法发生重载关系,会以类中方法为主

    某类已存在某个扩展方法,用户自定义扩展方法与该扩展方法发生重载,会以用户自定义扩展方法为主

    当然,上面这是自己研究定下的,若是不太准确,希望各位可以指正! 😄

    扩展方法接收可空类型

    上面,我们是定义的String类型扩展,当然,也可以给String?可空类型进行扩展方法

    这样写也是没有问题的:

    fun String?.isNullOrBlank(): Boolean {
        println("进入我们的方法里")
        return this == null || this.trim().length == 0
    }
    

    像这样,我们还可以直接给所有类型(Any类型)增加个toString()方法的扩展方法:

    fun Any?.toString(): String {
        if (this == null) return "null"
        // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
        // 解析为 Any 类的成员函数
        return toString()
    }
    

    但是,这个扩展方法是不起作用的!!

    为什么呢?因为Any对象已经存在了toString()此方法,根据上面的结论,会以类中的方法优先!

    扩展属性

    除了方法,我们也可以实现扩展属性

    语法

    val 类型.属性名: 属性类型名
        get() = 
    

    如有个示例,判断文件是否为md文件:

    val File.isMdFile: Boolean
        get() = extension.toLowerCase()=="md"
    

    使用:

    fun main() {
        val file =File("D:\\tt.md")
        println(file.isMdFile)
    }
    
    val File.isMdFile: Boolean
        get() = extension.toLowerCase()=="md"
    

    相关作用域与上述扩展方法讲解的是一致的,这里不再赘述

    扩展伴生对象

    这里,感觉就是类似工具类的扩展吧,如果使用伴生对象,之后就可以类名.方法名去调用方法(类似Java中的静态方法)

    如果我们想追加一些方法,也可以使用扩展来实现,如下例子

    class MyClass {
        companion object { }  // 将被称为 "Companion"
    }
    
    fun MyClass.Companion.printCompanion() { println("companion") }
    
    fun main() {
        MyClass.printCompanion()
    }
    

    原理补充

    Kotlin中的扩展函数,其实最后编译成class文件都会转为一个静态方法

    这一过程实际上是由Kotlin编译器替我们实现了,我们只管吃语法糖就完事了!

    我们以下面方法为例:

    fun String?.isNullOrBlank(): Boolean {
        println("进入我们的方法里")
        return this == null || this.trim().length == 0
    }
    

    最终生成的静态方法:

    // 这个类名就是顶层文件名+“Kt”后缀
    public final class ExtendsionDemoKt {
       // 扩展函数 isNullOrBlank 对应实际上是 Java 中的静态函数,并且传入一个接收者类型对象作为参数
       public static final boolean isNullOrBlank(@NotNull CharSequence $this$isNullOrBlank) {
          Intrinsics.checkParameterIsNotNull($this$isNullOrBlank, "$this$isNullOrBlank");
          String var1 = "进入我们的方法";
          boolean var2 = false;
          System.out.println(var1);
          return StringsKt.trim($this$isNullOrBlank).length() == 0;
       }
    }
    

    如果我们isNullOrBlank还有参数的话,静态方法中除了CharSequence这个参数,还会多出其他参数

    PS: 可以点开对应的class文件,然后使用tool->kotlin->Decompile Kotlin To Java,将class还原会java代码

    参考

  • 相关阅读:
    国庆假期买哪款耳机好?国庆假期必备蓝牙耳机推荐
    线代小课整理
    Golang可重入锁的实现
    java计算机毕业设计springboot+vue村委会管理系统
    buildadmin+tp8表格操作(4) Table组件,baTable类和 elementplus中的属性关系
    读取pdf、docx、doc、ppt、pptx并转为txt
    测量网络性能的开源工具iperf3
    我和谷歌共成长——前端必备插件Vue.js devtools
    Git远程仓库和多人协作介绍及使用
    随机森林计算指标重要性—从决策树到随机森林Python实现
  • 原文地址:https://www.cnblogs.com/stars-one/p/16440099.html