一篇小文理解Kotlin之扩展函数/属性

一篇小文理解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
}

JavaAPI中,并没有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
            [email protected]()//可以指定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。

    而不同的分发接收者调用,则会执行不同的扩展函数的具体。

你可能感兴趣的:(一篇小文理解Kotlin之扩展函数/属性)