7.12 嵌套类(Nested Class)

7.12.1 嵌套类:类中的类

类可以嵌套在其他类中,可以嵌套多层:

1. class NestedClassesDemo {
2. class Outer {
3. private val zero: Int = 0
4. val one: Int = 1

6. class Nested {
7. fun getTwo() = 2
8. class Nested1 {
9. val three = 3
10. fun getFour() = 4
11. }
12. }
13. }
14. }

测试代码:

1. val one = NestedClassesDemo.Outer().one
2. val two = NestedClassesDemo.Outer.Nested().getTwo()
3. val three = NestedClassesDemo.Outer.Nested.Nested1().three
4. val four = NestedClassesDemo.Outer.Nested.Nested1().getFour()
5. println(one)
6. println(two)
7. println(three)
8. println(four)

我们可以看出,访问嵌套类的方式是直接使用 类名., 有多少层嵌套,就用多少层类名来访问。

普通的嵌套类,没有持有外部类的引用,所以是无法访问外部类的变量的:

1. class NestedClassesDemo {
2. class Outer {
3. private val zero: Int = 0
4. val one: Int = 1

6. class Nested {
7. fun getTwo() = 2

9. fun accessOuter() = {
10. println(zero) // error, cannot access outer class
11. println(one)  // error, cannot access outer class
12. }
13. }
14. }
15. }

我们在Nested类中,访问不到Outer类中的变量zero,one。 如果想要访问到,我们只需要在Nested类前面加上inner关键字修饰,表明这是一个嵌套的内部类。

7.12.2 内部类(Inner Class)

类可以标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用:

1. class NestedClassesDemo {
2. class Outer {
3. private val zero: Int = 0
4. val one: Int = 1

6. inner class Inner {
7. fun accessOuter() = {
8. println(zero) // works
9. println(one) // works
10. }
11. }
12. }

测试代码:

1. val innerClass = NestedClassesDemo.Outer().Inner().accessOuter()

我们可以看到,当访问inner class Inner的时候,我们使用的是Outer().Inner(), 这是持有了Outer的对象引用。跟普通嵌套类直接使用类名访问的方式区分。

7.12.3 匿名内部类(Annonymous Inner Class)

匿名内部类,就是没有名字的内部类。既然是内部类,那么它自然也是可以访问外部类的变量的。

我们使用对象表达式创建一个匿名内部类实例:

1. class NestedClassesDemo {
2. class AnonymousInnerClassDemo {
3. var isRunning = false
4. fun doRun() {
5. Thread(object : Runnable {
6. override fun run() {
7. isRunning = true
8. println("doRun : i am running, isRunning = $isRunning")
9. }
10. }).start()
11. }
12. }
13. }

如果对象是函数式 Java 接口,即具有单个抽象方法的 Java 接口的实例,例如上面的例子中的Runnable接口:

1. @FunctionalInterface
2. public interface Runnable {
3. public abstract void run();
4. }

我们可以使用lambda表达式创建它,下面的几种写法都是可以的:

1. fun doStop() {
2. var isRunning = true
3. Thread({
4. isRunning = false
5. println("doStop: i am not running, isRunning = $isRunning")
6. }).start()
7. }

9. fun doWait() {
10. var isRunning = true

12. val wait = Runnable {
13. isRunning = false
14. println("doWait: i am waiting, isRunning = $isRunning")
15. }

17. Thread(wait).start()
18. }

20. fun doNotify() {
21. var isRunning = true

23. val wait = {
24. isRunning = false
25. println("doNotify: i notify, isRunning = $isRunning")
26. }

28. Thread(wait).start()
29. }

测试代码:

1. NestedClassesDemo.Outer.AnonymousInnerClassDemo().doRun()
2. NestedClassesDemo.Outer.AnonymousInnerClassDemo().doStop()
3. NestedClassesDemo.Outer.AnonymousInnerClassDemo().doWait()
4. NestedClassesDemo.Outer.AnonymousInnerClassDemo().doNotify()

输出:

1. doRun : i am running, isRunning = true
2. doStop: i am not running, isRunning = false
3. doWait: i am waiting, isRunning = false
4. doNotify: i notify, isRunning = false

关于lambda表达式以及函数式编程,我们将在下一章中学习。