Kotlin inner 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的类系统同时支持嵌套类和内部类。 inner 关键字用于创建可以访问其外部类成员的内部类。 本教程通过实际示例深入探讨了 inner 关键字。
基本定义
在 Kotlin 中,嵌套类默认是静态的,这意味着它不能访问外部类的成员。 inner 关键字创建了一个非静态嵌套类,该类维护对外部类实例的引用。 这允许访问外部类成员。
基本内部类示例
此示例演示了内部类的最简单用法。 内部类可以访问其外部类的属性和方法。
package com.zetcode
class Outer {
private val outerProperty = "Outer property"
inner class Inner {
fun printOuterProperty() {
println(outerProperty) // Can access outer class members
}
}
}
fun main() {
val outer = Outer()
val inner = outer.Inner()
inner.printOuterProperty() // Output: Outer property
}
在这里,Inner 类用 inner 关键字标记,允许它访问 Outer 类的私有 outerProperty。 如果没有 inner,则无法进行此访问。
内部类与嵌套类
此示例将内部类与常规嵌套类进行比较,以显示它们在行为和访问能力上的主要区别。
package com.zetcode
class Outer {
private val value = 10
class Nested {
fun getValue(): Int {
// return value // Would cause compilation error
return 0
}
}
inner class Inner {
fun getValue(): Int {
return value // Can access outer class members
}
}
}
fun main() {
val nested = Outer.Nested()
println(nested.getValue()) // Output: 0
val outer = Outer()
val inner = outer.Inner()
println(inner.getValue()) // Output: 10
}
Nested 类无法访问外部类的 value 属性,而 Inner 类可以。 这展示了 Kotlin 中常规嵌套类和内部类之间的根本区别。
访问外部类实例
内部类可以使用 this@Outer 语法引用其外部类实例。 当内部和外部类成员之间存在命名冲突时,这很有用。
package com.zetcode
class Outer {
private val name = "Outer"
inner class Inner {
private val name = "Inner"
fun printNames() {
println("Inner name: $name") // Inner's name
println("Outer name: ${this@Outer.name}") // Outer's name
}
}
}
fun main() {
val outer = Outer()
val inner = outer.Inner()
inner.printNames()
// Output:
// Inner name: Inner
// Outer name: Outer
}
此示例显示了当两个类都具有同名成员时,如何从内部类显式引用外部类实例。 this@Outer 语法提供了此功能。
多个内部类
单个外部类可以包含多个内部类。 每个内部类都可以访问外部类的成员。
package com.zetcode
class Computer {
private val brand = "ACME"
inner class CPU {
fun getBrand() = "CPU by $brand"
}
inner class RAM {
fun getBrand() = "RAM by $brand"
}
}
fun main() {
val computer = Computer()
val cpu = computer.CPU()
val ram = computer.RAM()
println(cpu.getBrand()) // Output: CPU by ACME
println(ram.getBrand()) // Output: RAM by ACME
}
Computer 类包含两个内部类:CPU 和 RAM。 它们都可以访问外部类的 brand 属性。 这演示了内部类如何共享对公共外部类状态的访问。
内部类继承
内部类可以参与继承层次结构。 它们可以扩展其他类或被其他类扩展。
package com.zetcode
open class Animal {
open fun sound() = "Generic animal sound"
}
class Zoo {
private val zooName = "City Zoo"
inner class Lion : Animal() {
override fun sound() = "Roar from $zooName"
}
}
fun main() {
val zoo = Zoo()
val lion = zoo.Lion()
println(lion.sound()) // Output: Roar from City Zoo
}
Lion 内部类扩展了 Animal 类,同时仍然可以访问 Zoo 外部类的 zooName 属性。 这显示了内部类如何参与继承,同时保留它们与外部类的连接。
带有接口实现的内部类
内部类可以实现接口,同时保持对外部类成员的访问。 这将接口多态性与内部类功能相结合。
package com.zetcode
interface Clickable {
fun click()
}
class Button {
private val label = "Submit"
inner class SubmitButton : Clickable {
override fun click() {
println("$label button clicked")
}
}
}
fun main() {
val button = Button()
val submit: Clickable = button.SubmitButton()
submit.click() // Output: Submit button clicked
}
SubmitButton 内部类实现了 Clickable 接口,同时访问了外部 Button 类的 label 属性。 这演示了内部类如何在履行接口契约的同时保持与外部类的连接。
内部类的局限性
虽然内部类很强大,但它们也有一些限制和注意事项
- 内存开销: 每个内部类实例都会维护对其外部类实例的引用。
- 序列化复杂性: 由于内部类对外部类的隐式引用,序列化内部类可能更复杂。
- 静态成员: 内部类不能声明静态成员或伴生对象。
- 初始化顺序: 外部类必须在其内部类之前初始化。
- 匿名内部类: Kotlin 使用对象表达式而不是 Java 中发现的匿名内部类。
内部类的最佳实践
- 谨慎使用: 仅当您需要访问外部类成员时才使用内部类。
- 考虑替代方案: 对于简单的情况,更喜欢常规嵌套类或顶级函数。
- 注意引用: 留意隐式的外部类引用,以防止内存泄漏。
- 清晰命名: 使用清晰的名称来区分内部和外部类成员。
- 记录关系: 清楚地记录内部类和外部类之间的关系。
来源
本教程深入介绍了 Kotlin 的 inner 关键字,展示了如何创建和使用可以访问其外部类成员的内部类。 我们探讨了各种情况,包括继承、接口实现和多个内部类。 正确使用内部类可以帮助组织相关功能,同时保持对共享状态的访问。
作者
列出 所有 Kotlin 教程。