06 扩展函数&扩展属性

212 阅读4分钟

扩展函数

Kotlin的扩展函数是一种特殊的函数,它可以为一个已有的类添加新的功能,而不需要修改或继承这个类。

写法

定义扩展函数

扩展函数的定义是在函数名前加上要扩展的类名和一个点号,例如:

fun String.lastChar(): Char {
    return this[this.length - 1]
}

这个函数为String类添加了一个lastChar()方法,它可以返回字符串的最后一个字符。注意,扩展函数并不会真的修改原来的类,它只是一种语法糖,让我们可以用点号调用这个函数,就像调用类的成员方法一样。

在何处定义扩展函数?

要为String类添加一个lastChar()方法,我们可以在任何地方定义一个扩展函数,只要它在我们需要使用它的地方是可见的。例如,我们可以在一个单独的文件中定义这个函数,然后在其他文件中导入它:

// Extensions.kt
package com.example

fun String.lastChar(): Char {
    return this[this.length - 1]
}

// Main.kt
package com.example

import com.example.lastChar // 导入扩展函数

fun main() {
    val s = "Hello"
    println(s.lastChar()) // o
}

或者,我们可以在一个类或对象中定义这个函数,然后在这个类或对象的作用域内使用它:

class StringUtil {
    companion object {
        fun String.lastChar(): Char {
            return this[this.length - 1]
        }
    }
}

fun main() {
    val s = "Hello"
    println(StringUtil.s.lastChar()) // o
}

注意,如果我们在一个类或对象中定义扩展函数,我们需要使用限定的this来访问receiver对象,即在this前面加上类或对象的名字。例如,StringUtil.this.lastChar()。

性能优化:如果想使用inline内联来进行性能优化,那么在定义高阶函数的地方加上inline就对了。

image.png

调用扩展函数

Kotlin中的扩展函数有一个叫做receiver的东西,它指的是被扩展的类的类型。在上面的例子中,String就是receiver类型,它表示这个函数是为String类扩展的。在扩展函数内部,我们可以使用this关键字来访问receiver对象,也就是调用这个函数的对象。例如:

val s = "Hello"
println(s.lastChar()) // 输出o

这里,s就是receiver对象,它是一个String类型的变量。我们可以用this来代替s,如this.lastChar(),但通常可以省略this。

好处

扩展函数的好处是可以让我们为任何类添加自定义的功能,而不需要修改源码或创建子类。这样可以让我们的代码更简洁、更易读、更灵活。

限制

Kotlin的扩展函数有以下几个限制:

  • 扩展函数不能访问被扩展类的私有或受保护的成员,因为它们并不是真正的类成员,只是一种外部函数。
  • 扩展函数是静态解析的,也就是说,它们不是多态的。调用哪个扩展函数只取决于变量的声明类型,而不是运行时的实际类型。
  • 扩展函数不能被重写,也就是说,如果一个子类定义了一个和父类相同的扩展函数,它并不会覆盖父类的扩展函数。
  • 扩展函数不能和类成员函数同名,否则会造成歧义。如果一个类有一个成员函数和一个扩展函数,它们有相同的接收者类型、相同的名字,并且适用于给定的参数,那么总是优先调用成员函数。

扩展属性

和Kotlin的扩展函数相似,Kotlin的扩展属性是一种特殊的属性,它可以为一个已有的类添加新的属性,而不需要修改或继承这个类。

写法

定义扩展属性

扩展属性的定义是在属性名前加上receiver类型和一个点号,例如:

val String.lastChar: Char
    get() = this[this.length - 1]

这个属性为String类添加了一个lastChar属性,它可以返回字符串的最后一个字符。这里,String就是receiver类型,它表示这个属性是为String类扩展的。

调用扩展属性

在扩展属性内部,我们可以使用this关键字来访问receiver对象,也就是调用这个属性的对象。例如:

val s = "Hello"
println(s.lastChar) // o

这里,s就是receiver对象,它是一个String类型的变量。我们可以用this来代替s,如this.lastChar,但通常可以省略this。

扩展属性和扩展函数的相似之处

扩展属性和扩展函数有以下几个相似之处:

  • 它们都需要指定receiver类型和receiver对象。
  • 它们都不能访问被扩展类的私有或受保护的成员。
  • 它们都是静态解析的,也就是说,它们不是多态的。
  • 它们都不能和类成员同名,否则会造成歧义。

扩展属性和扩展函数的不同之处

扩展属性和扩展函数有以下几个不同之处:

  • 扩展属性不能有初始化器,也就是说,它们不能有=后面的表达式。
  • 扩展属性必须有显示定义的getter函数,如果是可变属性,还必须有setter函数。
  • 扩展属性不能有幕后字段(backing field),也就是说,它们不能使用field关键字来访问存储值。