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 教程。