Menu
CoddyTech

扩展函数

本课属于 Coddy Kotlin 学习旅程的“面向对象编程”单元。第 30 课,共 57 课。

扩展函数可以为现有类型添加函数,而无需更改该类型的源代码。它的名称写在接收者类型之后,在函数内部,this 表示接收者,也可以省略:

fun String.shout() = uppercase() + "!"
fun Int.isEven() = this % 2 == 0

在 main 内部:

println("hello".shout())
println(7.isEven())
println(listOf(1, 2, 3, 4).count { it.isEven() })

输出:

HELLO!
false
2

扩展也适用于你自己创建的类,扩展属性可以添加计算属性。扩展无法添加存储状态,并且只能访问接收者的公共成员:

class Temperature(val celsius: Double)

val Temperature.fahrenheit: Double
    get() = celsius * 9 / 5 + 32

fun Temperature.describe() = "$celsius C is $fahrenheit F"

在 main 中:

println(Temperature(100.0).describe())

输出:

100.0 C is 212.0 F

扩展是以静态方式解析的:表达式的声明类型决定了在编译程序时使用哪个扩展,而不是对象的类。这是扩展与被重写的成员之间的主要区别:

open class Shape {
    open fun member() = "shape member"
}
class Circle : Shape() {
    override fun member() = "circle member"
}

fun Shape.ext() = "shape extension"
fun Circle.ext() = "circle extension"

在 main 中:

val s: Shape = Circle()
println(s.member())
println(s.ext())

输出:

circle member
shape extension

如果类中已经有一个签名相同的成员,那么总是优先使用该成员。对于属于该类型且可能被重写的行为,使用成员;对于从外部添加的辅助功能,例如为你不拥有的类添加格式化功能,则使用扩展:

class Robot {
    fun greet() = "member greet"
}

fun Robot.greet() = "extension greet"
fun Robot.wave() = "extension wave"

在 main 中:

val r = Robot()
println(r.greet())
println(r.wave())

输出:

member greet
extension wave
challenge icon

挑战

简单

Money(cents) 已在 Money.kt 中提供。请在 MoneyExtensions.kt 中为它编写扩展:扩展属性 Int.dollars 和 Int.cents,用于将数字转换为 Money(12.dollars 是 1200 美分);Money.format(),用于返回 $12.50(负数金额则返回 -$3.05);List<Money>.total(),用于将总和作为 Money 返回;以及 Money.split(parts),用于将金额拆分为 parts 份,各份之间最多相差 1 美分,较大的金额排在前面。

提供的代码会读取若干行 12 dollars 或 50 cents,以及最后一行中的份数。它会输出每笔金额、total: $12.50 和 split 3: $4.17 $4.17 $4.16。

你的代码应写在 MoneyExtensions.kt 和 Money.kt 中。Main.kt 包含提供的输入/输出代码,不能修改。

亲手试试

fun main() {
    // 提供的输入和输出代码:保持原样
    val input = generateSequence(::readLine).toList()
    val amounts = input.dropLast(1).map { line ->
        val (n, unit) = line.split(" ")
        if (unit == "dollars") n.toInt().dollars else n.toInt().cents
    }
    for (m in amounts) println(m.format())
    val total = amounts.total()
    println("total: ${total.format()}")
    val parts = input.last().toInt()
    println("split $parts: " + total.split(parts).joinToString(" ") { it.format() })
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Kotlin 在线编辑器