每天学一点 Kotlin -- 函数:扩展

2021-10-27  本文已影响0人  冯可乐同学

----《第一季Kotlin崛起:次世代Android开发 》学习笔记

总目录:每天学一点 Kotlin ---- 目录
上一篇:每天学一点 Kotlin -- 函数:操作符
下一篇:每天学一点 Kotlin -- 函数:字面量

1. 函数扩展

1.1 由来:有时候很想对现有的类进行改进,加入新的函数,但是苦于无权限更改或者没有源代码。有一种方法就是继承这个类,并在子类实现上增加函数,但是这个方法并不总是有效。因为有些类可以已经是 final 不可能继承的。而且没法对既有类型的实例进行控制。另一种方法是单独新建一个文件,把已有类型的实例放进自定义的函数里解决,从而行形成一个工具类或帮助类。但是这样层层的封装在工程量比较大的时候也并不是很方便。

1.2 虽然 Kotlin 根 Java 语法不同,但是和 Java 一样还是面向对象编程的思想为主。可是对上面提到的问题面向对象编程也不能很好地解决时,这时候扩展就很有用了。

1.3 扩展有很多种,其中函数扩展是比较常用的,扩展函数就像顶层函数一样定义。 举个栗子:

fun Int.square() = this * this
3.square()

1.4 如上所示,定义函数扩展也很简单,只要在函数名前面加上类型和点。这个被扩张的类型,称为接收者类型。也就是说这个接收者类型被进行了函数扩展。this 代表类型实例的引用。

2. 扩展函数的优先级

2.1 扩展函数不能重载类或接口中已经定义的函数。如果你定义了一个与既有函数一模一样的扩展函数,名字一样,参数一样,参数的顺序一样,返回值一样,这个扩张函数就是无效的。能重载那就不是扩展了,那是子类继承。因为在编译过程中,编译器会首先去接收者类型的所有父类和接口中去查找有没有相同的成员函数,如果有,则使用。
2.2 因此在做扩展函数之前,尽量确保类型中无此功能的函数,名字也尽量起的个人化一点。

3. 扩展函数的作用范围

3.1 通常用顶层函数做扩展,但也可以在类中扩展,这样在想限制扩展的范围时有用:

class MyNumber(var k: Int) {
    private fun Int.triple() = this * this * this  
    // Int.triple() 只能在MyNumber类之内生效,之外是无法使用的
    
    fun addFactor(p: Int) {
        k += p.triple()
    }
}

4. 扩展函数在子类中的重载

4.1 对父类或接口中的同名扩展是无效的,但是子类可以重载成员扩展函数,前提是这个类是 open 的,即可以重载的。在这种情况下,子类的函数接收者类型是由运行时的实例决定的,而扩展的接收者类型始终是编译时就决定的,也就是静态的。举个栗子:

open class Particle()

class Electron : Particle()

open class Element(val name: String) {
    open fun Particle.react(name: String): Unit {
        println("$name 与粒子发生反应")
    }

    open fun Electron.react(name: String): Unit {
        println("$name 与电子发生反应生成同位素")
    }

    fun react(particle: Particle): Unit {
        particle.react(name)
    }
}

class NobleGas(name: String) : Element(name) {
    override fun Particle.react(name: String): Unit {
        println("$name 是稀有气体,不与粒子发生反应")
    }

    override fun Electron.react(name: String): Unit {
        println("$name 是稀有气体,不与电子发生反应")
    }

    fun react(particle: Electron): Unit {
        particle.react(name)
    }
}

//在main函数中调用:
fun main(args: Array<String>) {
    val al = Element("铝")
    al.react(Particle())
    al.react(Electron())

    val neon = NobleGas("氩")
    neon.react(Particle())
    neon.react(Electron())
}

打印结果:

铝 与粒子发生反应
铝 与粒子发生反应
氩 是稀有气体,不与粒子发生反应
氩 是稀有气体,不与电子发生反应

5. infix 中缀函数

5.1 中缀函数跟赋值操作符有点像,不同的是名称可以是任意的
5.2 举个栗子:Kotlin 中自带的 to 函数可以把两个变量凑成一个二元组(Pair):

fun testInfix() {
    val train = "北京" to "上海"
    println(train.first)
    println(train.second)
}

5.3 在函数关键字 fun 前面添加 infix 关键字就可以实现自定义中缀函数,而且只有一个入参

5.4 Kotlin 中可以把成员函数定义成中缀函数。因为中缀函数是二元的,必须有两个参数,第一个参数是实例,第二个是函数的入参。举个栗子:

infix fun String.到(destination: String): String {
    return "这是从 -" + this + "- 开往 -" + destination + "- 的火车"
}

fun main() {
    val str1 = "北京" 到 ("上海")
    val str2 = "北京" 到 "上海"
    println(str1)
    println(str2)
}

打印结果:

这是从 -北京- 开往 -上海- 的火车
这是从 -北京- 开往 -上海- 的火车
相关代码:https://gitee.com/fzq.com/test-demo
上一篇下一篇

猜你喜欢

热点阅读