ZetCode

Kotlin inner 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的类系统同时支持嵌套类和内部类。 inner 关键字用于创建可以访问其外部类成员的内部类。 本教程通过实际示例深入探讨了 inner 关键字。

基本定义

在 Kotlin 中,嵌套类默认是静态的,这意味着它不能访问外部类的成员。 inner 关键字创建了一个非静态嵌套类,该类维护对外部类实例的引用。 这允许访问外部类成员。

基本内部类示例

此示例演示了内部类的最简单用法。 内部类可以访问其外部类的属性和方法。

BasicInner.kt
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,则无法进行此访问。

内部类与嵌套类

此示例将内部类与常规嵌套类进行比较,以显示它们在行为和访问能力上的主要区别。

InnerVsNested.kt
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 语法引用其外部类实例。 当内部和外部类成员之间存在命名冲突时,这很有用。

OuterInstance.kt
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 语法提供了此功能。

多个内部类

单个外部类可以包含多个内部类。 每个内部类都可以访问外部类的成员。

MultipleInner.kt
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 属性。 这演示了内部类如何共享对公共外部类状态的访问。

内部类继承

内部类可以参与继承层次结构。 它们可以扩展其他类或被其他类扩展。

InnerInheritance.kt
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 属性。 这显示了内部类如何参与继承,同时保留它们与外部类的连接。

带有接口实现的内部类

内部类可以实现接口,同时保持对外部类成员的访问。 这将接口多态性与内部类功能相结合。

InnerInterface.kt
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 嵌套类和内部类文档

本教程深入介绍了 Kotlin 的 inner 关键字,展示了如何创建和使用可以访问其外部类成员的内部类。 我们探讨了各种情况,包括继承、接口实现和多个内部类。 正确使用内部类可以帮助组织相关功能,同时保持对共享状态的访问。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有多年的编程经验。 自 2007 年以来,我一直在撰写编程文章。 到目前为止,我写了 1400 多篇文章和 8 本电子书。 我拥有超过八年的编程教学经验。

列出 所有 Kotlin 教程