002-kotlin-nested-class
Kotlin 中的嵌套类(Nested Class)与内部类(Inner Class)
在 Kotlin 中,定义在另一个类内部的类分为两种:
- 嵌套类(Nested Class):默认,不持有外部类引用,相当于 Java 的
static嵌套类。 - 内部类(Inner Class):用
inner修饰,持有外部类引用,相当于 Java 的非静态内部类。
这是 Kotlin 与 Java 的一个重要区别,也是面试高频考点。
1. 嵌套类(Nested Class)
定义:直接写在类内部的类,没有 inner 关键字。
特点:
- 默认是静态的,不持有外部类实例引用。
- 不能访问外部类的成员(属性、方法)。
- 可以访问外部类的伴生对象(
companion object)成员。 - 实例化:
Outer.Nested()。
代码示例:
1 | class Outer { |
对应 Java 写法:
1 | public class Outer { |
Kotlin 的嵌套类默认就是 Java 的 static 嵌套类。
2. 内部类(Inner Class)
定义:用 inner 关键字修饰的嵌套类。
特点:
- 持有外部类实例的引用。
- 可以直接访问外部类的所有成员(包括私有成员)。
- 实例化必须依赖外部类实例:
outer.Inner()。 - 内部类中可用
this@Outer显式引用外部类实例。 - 可能引起内存泄漏(如 Android 中 Handler、Listener)。
代码示例:
1 | class Outer { |
对应 Java 写法:
1 | public class Outer { |
3. 与 Java 的对比
| 特性 | Kotlin 嵌套类 | Kotlin 内部类 | Java 静态嵌套类 | Java 非静态内部类 |
|---|---|---|---|---|
| 关键字 | 无 | inner |
static class |
无(默认) |
| 持有外部引用 | 否 | 是 | 否 | 是 |
| 访问外部实例成员 | 不能 | 能 | 不能 | 能 |
| 实例化方式 | Outer.Nested() |
outer.Inner() |
new Outer.Nested() |
outer.new Inner() |
| 内存泄漏风险 | 低 | 高 | 低 | 高 |
记忆口诀:
Kotlin 默认嵌套类就是 Java 的静态嵌套类;需要访问外部类实例时,加 inner。
4. 匿名内部类
Kotlin 中使用 object 表达式创建匿名内部类:
1 | button.setOnClickListener(object : View.OnClickListener { |
如果不需要持有外部引用,可以用 lambda:
1 | button.setOnClickListener { v -> ... } |
在 Android 中,匿名内部类若在 Activity 中,可能持有 Activity 引用导致泄漏,需注意。
5. 使用场景
- 嵌套类:组织辅助类、工具类,不需要访问外部类状态。例如
Map.Entry、Builder。 - 内部类:需要访问外部类状态,如 Adapter 的 ViewHolder、事件监听器、迭代器。
- 伴生对象:
companion object也是嵌套的一种,用于静态成员。
6. 常见面试题与答案
Q1:Kotlin 中嵌套类和内部类的区别?
A:嵌套类默认静态,不持有外部引用,不能访问外部实例成员;内部类用 inner 修饰,持有外部引用,可访问外部所有成员。
Q2:为什么 Kotlin 默认嵌套类是静态的?
A:为了减少内存泄漏风险,默认不持有外部引用,更安全。需要时显式加 inner。
Q3:如何在内部类中访问外部类实例?
A:直接访问即可;若命名冲突,用 this@Outer。
Q4:Kotlin 嵌套类能访问外部类的伴生对象吗?
A:能,因为伴生对象是静态的。
Q5:Java 如何调用 Kotlin 的嵌套类和内部类?
- 嵌套类:
new Outer.Nested() - 内部类:
outer.new Inner()
Q6:内部类为什么可能内存泄漏?
A:内部类持有外部类引用,若内部类实例生命周期长于外部类,外部类无法回收。
Q7:Kotlin 中 object 表达式和内部类的关系?
A:object : Interface {} 创建匿名内部类,若在外部类中会持有外部引用;可用 lambda 避免。
7. 代码示例:完整对比
1 | class Outer { |
8. 总结
- Kotlin 嵌套类默认静态,不持有外部引用;内部类用
inner,持有外部引用。 - 嵌套类不能访问外部实例成员,内部类可以。
- 实例化:
Outer.Nested()vsouter.Inner()。 - 内部类有内存泄漏风险,Android 中需注意。
- 与 Java 对比:Kotlin 默认嵌套类 = Java 静态嵌套类;Kotlin 内部类 = Java 非静态内部类。
- 面试常考区别、内存泄漏、
this@Outer、Java 互操作。