伴生对象
本课属于 Coddy Kotlin 学习旅程的“面向对象编程”单元。第 13 课,共 57 课。
有些成员属于整个类,而不是某个实例。Kotlin 会将它们放在类中的 companion object 里,你可以通过类名调用它们:
class Temperature(val celsius: Double) {
companion object {
fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
}
}在 main 中:
val boiling = Temperature.fromFahrenheit(212.0)
println(boiling.celsius)输出:
100.0fromFahrenheit 是一个工厂函数:一种用于创建实例的函数。它与构造函数不同,工厂函数有一个描述性名称,而且可以返回 null,而不是对象。使用 private constructor 后,工厂函数就是唯一的入口,因此可以先检查并清理输入:
class Username private constructor(val text: String) {
companion object {
fun of(raw: String): Username? {
val clean = raw.trim().lowercase()
return if (clean.length in 3..12) Username(clean) else null
}
}
}在 main 中:
println(Username.of(" Ada_L ")?.text)
println(Username.of("x")?.text)
// Username("x") // 错误:构造函数是私有的输出:
ada_l
null伴生对象属于类,因此可以调用私有构造函数,而类自己的代码可以不加前缀地使用伴生对象的成员。这里,每张票都会从所有票共用的一个计数器中取得下一个编号:
class Ticket(val holder: String) {
val number = nextNumber++
companion object {
private var nextNumber = 1
val issued: Int
get() = nextNumber - 1
}
}在 main 中:
val a = Ticket("Ada")
val b = Ticket("Bo")
println("${a.holder} #${a.number}, ${b.holder} #${b.number}")
println("issued: ${Ticket.issued}")输出:
Ada #1, Bo #2
issued: 2Kotlin 没有 static 关键字。伴生对象的成员可以实现类似的功能:整个类共用一份,通过 ClassName.member 访问。和任何对象一样,伴生对象只会在类首次使用时创建一次。把不依赖某个实例的内容放在其中,例如工厂函数和共享计数器;读取实例数据的内容则应保留为普通成员。
class User private constructor(val name: String) {
fun greet() = "hi $name" // 需要一个实例:user.greet()
companion object {
fun guest() = User("guest") // 属于类:User.guest()
}
}挑战
简单补全类 Coupon。它的构造函数是私有的,因此要通过伴生对象中的工厂函数 Coupon.create(code, percent) 创建优惠券。除非代码由 4 到 10 个大写字母和数字组成,且折扣百分比在 1 到 90 之间,否则它会返回 null。伴生对象还会在 created 中统计已创建的优惠券数量,外部代码可以读取这个数量,但不能修改它。apply(price) 会返回减去折扣后的价格,并向下取整(price * (100 - percent) / 100)。
提供的代码会读取类似 SAVE10 10 200 的行(代码、百分比、价格)。它会输出 SAVE10: 180 或 invalid coupon SAVE10,最后输出 created: 和优惠券数量。
请将代码写在 Coupon.kt 中。Main.kt 包含提供的输入/输出代码,不能编辑。
亲手试试
fun main() {
// 已提供的输入和输出代码:保持原样。
val input = generateSequence(::readLine).toList()
for (line in input) {
val (code, percent, price) = line.split(" ")
val coupon = Coupon.create(code, percent.toInt())
if (coupon == null) println("invalid coupon $code") else println("${coupon.code}: ${coupon.apply(price.toInt())}")
}
println("created: ${Coupon.created}")
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Kotlin 在线编辑器