Nested and inner classes
Nested and inner classes

Nested and inner classes

提供方: JetBrains 学院

如果你想创建一个超级英雄,你会怎么做?当然,你会打开你最喜欢的开发环境,创建一个 Superhero 类!

根据情况,我们的超级英雄需要一套特殊物品,例如魔法斗篷或锤子。这里可能会出现一个问题:如何最好地组织描述这些装备的类别?以及如何表明只有 Superhero 类别才能使用这些物品?

这时,我们的救星出现了——类。它们可以帮助我们按逻辑对类进行分组,并提高代码的封装性。

什么是嵌套类?

你可以在一个类中创建一个类,这样的类被称为 嵌套类 。

来看看我们的超级英雄:

class Superhero {
    class MagicCloak {
    }

    class Hammer {
    }
}

MagicCloak 和 Hammer 这两个类都是嵌套类。Superhero Superhero 通常被称为 , 而嵌套类及其属性、函数和构造函数都是外部类的 成员 。

问题在于, MagicCloak 和 Hammer 实际上并不与 Superhero 绑定,它们只是职业而已:

class Superhero {
    val power = 1000

    class MagicCloak {
        // you cannot access something from Superhero here
        val magicPower = 100
    }
    // you need to create a MagicCloak object to access its members
    val magicPower = power * MagicCloak().magicPower

    class Hammer {
        // you cannot access power property from Superhero here
        val mightPower = 100
    }
    val mightPower = power * Hammer().mightPower
}

如果需要在 Superhero 类之外使用 MagicCloak 和 Hammer ,则必须创建相应的对象:

val cloak = Superhero.MagicCloak()
val hammer = Superhero.Hammer()

如您所见,简单的嵌套类与外部类实际上并没有关联。在本主题中,我们将重点讨论嵌套类的一个特殊情况——。

内层阶级

普通的嵌套类不能访问其外部类的成员。但是,标记为 内部类的嵌套类可以访问其外部 类的成员。

我们来看另一个例子。假设你要编写一个名为 Cat 类来表示猫。猫可能有很多属性和功能,但我们也可能使用内部类结构。假设你想让一只猫戴上蝴蝶结,那么你需要创建一个新的 Bow 。这个 Bow 类需要非常小且具体,而且你知道没有猫就不会有蝴蝶结。解决方案是在 Cat 类内部创建一个 Bow 类:

class Cat(val name: String) {
    inner class Bow(val color: String) {
        fun printColor() {
            println("The cat named $name has a $color bow.")
        }
    }
}

我们来创造一只名叫鲍勃、系着红色蝴蝶结的猫:

fun main() {
    val cat: Cat = Cat("Bob")
    val bow: Cat.Bow = cat.Bow("red")

    bow.printColor()
}

我们创建了一个 Cat 实例,然后使用一种非常有趣的语法创建了一个 Bow 实例。

上述代码的输出结果为:

The cat named Bob has a red bow.

请记住,要使用内部类,我们必须创建外部类的实例 。 在我们的示例中,我们创建了一个 Cat 。您可以在外部类中自由使用内部类:

class Cat(val name: String) {
    inner class Bow(val color: String) {
        fun printColor() {
            println("The cat named $name has a $color bow.")
        }
    }
    val catBow = Bow("Green")
}

内部类的作用域

现在我们来讨论一下从内部类中可以看到什么,以及谁可以从外部访问内部类。

这是我们的 Cat 类,其中包含一个新函数 sayMeow ,以及一个内部类 Bow ,其中包含一个新函数 putOnABow 。

class Cat(val name: String) {
    fun sayMeow() {
        println("$name says: \"Meow\".")
    }

    inner class Bow(val color: String) {
        fun putOnABow() {
            sayMeow()
            println("The bow is on!")
        }

        fun printColor() {
            println("The cat named $name has a $color bow.")
        }
    }
}

你可以看到,在 Bow 类中,我们可以访问 Cat 类的所有成员: name 属性和 sayMeow 函数。

为了证明我们的代码有效,我们不妨创建一个名叫“公主”并系上金色蝴蝶结的猫咪?

fun main() {
    val cat: Cat = Cat("Princess")
    val bow: Cat.Bow = cat.Bow("golden")

    bow.printColor()
    bow.putOnABow()
}

是的,弓已经搭好了!

The cat named Princess has a golden bow.
Princess says: "Meow".
The bow is on!

你可能会遇到内部类成员与其外部类同名的情况。例如, Cat 和 Bow 都可能拥有 color 属性。在这种情况下,如何从内部类访问外部呢? 限定的 this 表达式可以帮到你!在内部类代码中写入 this@Cat.color 即可获取外部类的颜色值,而使用 color 或 this.color 则始终返回当前类的 color 属性值。

class Cat(val name: String, val color: String) {
    inner class Bow(val color: String) {
        fun printColor() {
            println("The cat named $name is ${this@Cat.color} and has a $color bow.")
        }
    }
}

现在让我们把所有规则汇总起来!

内类规则

内部类可以访问其外部类的所有成员。

要访问内部类,需要先实例化外部类,因为内部类与其包含类的相关联。在下面的示例中,内部类 Inner 的构造函数被调用,并传入包含类的实例:

val outer = Outer()
val inner = outer.Inner()

使用内部类的理由

你注意到我们的 Superhero 和 Cat 例子有什么共同点了吗?当然注意到了——我们都对外部世界隐藏了内部类。这提高了代码的 封装性 ——现在只有 Cat 才能戴蝴蝶结了。

使用内部类结构,您可以更方便地 组织 代码。所有 Superhero 的魔法装备都集中在一个地方,这样更容易在类之间切换,也更容易理解代码结构。

结论

  • 在一个类内部声明的类称为嵌套类。
  • 内部类是嵌套类的一种特殊情况,它可以访问其外部类的成员。
  • 内部类携带对外部类对象的引用,因此要使用内部类,我们必须先实例化一个外部类。
  • 内部类的主要思想是隐藏部分代码,使其不被其他类直接访问,从而提高封装性。

272 名学习者喜欢这篇理论文章, 8 名学习者不喜欢。 你呢?

报告拼写错误

相关条目

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注