一篇小文理解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
            this@BB.info()//可以指定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。

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,139评论 9 118
  • 扩展 Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。 这通过叫做扩展的特殊声明...
    戏先生阅读 229评论 0 0
  • 概述 Kotlin是面向对象的静态类型语言; 在Kotlin中,所有东西都是对象,在这个意义上可以在任意变量上调用...
    CodeMagic阅读 380评论 0 0
  • 类与继承 类 Kotlin 中使用关键字class声明类 classInvoice{/*……*/} 类声明由类名、...
    戏先生阅读 409评论 0 1
  • 自从实习结束后直到现在将近一年多的时间再也没有用过kotlin, 在今年五月份I/O大会上,Google再次明确了...
    Scus阅读 1,351评论 0 0