作用域扩展与伴生对象扩展
限制扩展的可见性,并为伴生对象添加扩展。
作用域扩展与伴生对象扩展 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。
为什么要使用作用域扩展
默认情况下,只要导入扩展,就可以在任何地方看到它。限定作用域可以将可见性限制在类、文件或上下文中,从而减少命名空间污染并提高可发现性。
文件私有扩展
在文件级别将扩展函数标记为 private,即可将其作用域限制为该文件。
private fun String.shout() = uppercase() + "!"
fun main() {
println("hello".shout()) // HELLO!
}内部可见性
使用 internal 将扩展限制在当前模块内,这非常适合不打算供外部使用的库内部功能。
internal fun List<Int>.sumIfPositive(): Int = filter { it > 0 }.sum()
fun main() {
println(listOf(-1, 2, 3, -4).sumIfPositive()) // 5
}成员扩展函数
请在类内部声明扩展,使其仅在该类的实例处于作用域内时可用。它有两个接收者:分派接收者(所属类)和扩展接收者(目标对象)。
class StringHelper(val prefix: String) {
fun String.withPrefix() = prefix + this
fun build(): String = "world".withPrefix() // calls extension via instance
}
fun main() {
println(StringHelper("hello ").build()) // hello world
}成员扩展的可见性
从分派类外部无法访问成员扩展。这对于封装很有用。
class Repo {
fun List<Int>.allPositive() = all { it > 0 }
fun check(xs: List<Int>) = xs.allPositive()
}
fun main() {
val r = Repo()
println(r.check(listOf(1, 2, 3))) // true
// listOf(1).allPositive() // ERROR — no access
}伴生对象上的扩展
向类的伴生对象添加扩展。对调用者来说,它看起来像静态方法,但实际位于类外部。
class Greeter {
companion object
}
fun Greeter.Companion.morning() = "Good morning!"
fun main() {
println(Greeter.morning())
}为什么使用伴生对象扩展
这对于从原始类外部添加工厂方法或辅助函数很有用,而且无需修改原始类。
class User(val name: String) {
companion object
}
fun User.Companion.guest() = User("Guest")
fun User.Companion.system() = User("System")
fun main() {
println(User.guest().name)
println(User.system().name)
}必须存在空的伴生对象
类必须声明一个伴生对象(即使它为空),伴生对象扩展才能附加到它上面。否则,扩展目标不存在。
class Box {
companion object // required, even if empty
}
fun Box.Companion.create(): Box = Box()
fun main() {
println(Box.create())
}通过导入限定扩展作用域
您可以只在特定文件中导入扩展,从而限定其使用范围。不导入它的文件无法看到它。
// File A:
// fun String.shout() = uppercase()
// File B imports:
// import com.example.shout
// Only File B can call shout()
fun String.shout() = uppercase() + "!"
fun main() { println("hi".shout()) }通过作用域函数使用扩展函数
使用 with 或 apply,可以临时将对象引入作用域以使用扩展。
class StringTools {
fun String.greet() = "Hello, $this!"
fun process(name: String) = with(this) {
name.greet()
}
}
fun main() {
println(StringTools().process("Ada"))
}伴生对象上的扩展属性
伴生对象扩展也可以是属性——从类外部添加命名常量。
class HttpStatus {
companion object
}
val HttpStatus.Companion.OK_CODE: Int get() = 200
val HttpStatus.Companion.NOT_FOUND: Int get() = 404
fun main() {
println(HttpStatus.OK_CODE)
println(HttpStatus.NOT_FOUND)
}最佳实践
使用 private 定义文件级辅助函数,使用 internal 定义仅限模块使用的 API,使用成员扩展实现封装,并使用伴生对象扩展从外部添加工厂式辅助函数。
class StringUtils {
companion object
}
fun StringUtils.Companion.isAlpha(s: String) = s.all { it.isLetter() }
fun main() {
println(StringUtils.isAlpha("hello"))
println(StringUtils.isAlpha("hi5"))
}快速检查
要从外部为某个类添加伴生对象扩展,该类必须具备什么?
回顾
在类内部使用 private、internal 或成员扩展限制扩展的作用域。从外部为类添加伴生对象扩展(函数或属性)时,应将其附加到该类声明的伴生对象上。这样可以减少命名空间污染,同时保持代码模块化。
常见问题解答
「作用域扩展与伴生对象扩展」课时是免费的吗?
是的 — 「作用域扩展与伴生对象扩展」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。
「作用域扩展与伴生对象扩展」这节课中我会学到什么?
限制扩展的可见性,并为伴生对象添加扩展。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Kotlin Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「作用域扩展与伴生对象扩展」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Kotlin Academy 课中编写并运行代码吗?
能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。