Skip to content

类、属性与接口

源:Kotlin 类系统参考

Kotlin 的类系统极度精简。它通过主构造函数属性代理默认 final 等特性,强制引导开发者编写更安全的代码。

构造体系

Kotlin 区分 主构造函数 (Primary)次构造函数 (Secondary)

kotlin
// 1. 直接在类头定义参数
// 2. var/val 参数自动成为属性
class User(val id: Int, var name: String) {
    
    // 初始化块 (Init Block):配合主构造函数使用
    init {
        require(id > 0) { "Invalid ID" }
        println("User $name created")
    }
}
kotlin
class View : View {
    // 必须委托给主构造函数 (如果存在) 或其他次构造函数
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

属性 (Properties)

Kotlin 的属性不仅仅是字段,它自带 Getter/Setter 钩子。

幕后字段 (Backing Field)

当你自定义 Setter 时,必须使用 field 关键字来访问实际存储值的变量。

kotlin
var counter = 0
    set(value) {
        if (value >= 0) {
            // ❌ counter = value (死循环!)
            field = value // ✅ 正确
        }
    }

显式幕后字段 (Explicit Backing Fields) Kotlin 2.3

在 Kotlin 2.3 之前,如果我们想让属性对外只读(Immutable),对内可变(Mutable),通常需要定义两个属性(也就是著名的“下划线命名法”):

kotlin
// ❌ 旧写法:样板代码多,命名空间污染
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> 
    get() = _uiState

Kotlin 2.3.0 引入了显式幕后字段,允许我们在属性内部直接声明 field,并赋予它不同的类型。

kotlin
// ✅ 新写法:干净、内聚
val uiState: StateFlow<UiState>
    // 显式定义 field,类型为 MutableStateFlow,但属性类型是 StateFlow
    field = MutableStateFlow(UiState()) 
    get() = field
    // 可以在类内部直接访问 field 进行修改

继承与多态

默认 Final

Kotlin 类默认是 final 的(不可继承)。要允许继承,必须添加 open 关键字。

kotlin
open class Base {
    open fun v() {}
    fun nv() {} // 默认为 final,不可覆盖
}

class Derived : Base() {
    override fun v() {}
}

super 歧义处理

当继承的多个父类(如接口)有同名默认实现时,必须显式指定 super<T>

kotlin
interface A { fun foo() = print("A") }
interface B { fun foo() = print("B") }

class C : A, B {
    override fun foo() {
        super<A>.foo() // 调用 A.foo()
        super<B>.foo() // 调用 B.foo()
    }
}

枚举类 (Enum Classes)

枚举类用于定义类型安全的常量集合。

kotlin
enum class Direction {
    NORTH, SOUTH, WEST, EAST
}

属性与方法

枚举不仅是常量,还可以拥有属性和方法。

kotlin
enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0),
    GREEN(0, 255, 0); // 必须用分号结束常量列表

    fun rgb() = (r * 256 + g) * 256 + b
}

匿名类 (Anonymous Classes)

枚举常量可以声明自己的匿名类来覆盖基类方法。

kotlin
enum class Protocol {
    HTTP {
        override fun signal() = "CONNECT"
    },
    FTP {
        override fun signal() = "TRANSFER"
    };

    abstract fun signal(): String
}

现代遍历:entries (Kotlin 1.9+)

传统的 values() 方法会每次创建一个新数组,存在性能开销。推荐使用 entries 属性,它返回一个不可变的预分配 List。

kotlin
// ❌ 旧写法 (内存开销)
// for (color in Color.values()) { ... }

// ✅ 新写法 (高效)
for (color in Color.entries) { 
    println(color) 
}

嵌套类 vs 内部类

这是 Android 开发中 内存泄漏 的高发区。

关键字对应 Java持有外部引用?风险
无 (默认)static class安全
innerclass高风险 (可能导致 Activity 泄漏)

内存泄漏警告

在 Activity/Fragment 中使用 inner class (如 Handler, Adapter) 时,如果该内部类的生命周期长于外部类(例如被异步线程持有),会导致 Activity 无法被 GC 回收。 建议:默认使用嵌套类(不加 inner)。

kotlin
class Outer {
    private val bar: Int = 1
    
    // 嵌套类:无法访问 bar
    class Nested {
        fun foo() = 2
    }
    
    // 内部类:可以访问 bar
    inner class Inner {
        fun foo() = bar
    }
}

函数式接口 (Functional Interface)

如果接口只有一个抽象方法,标记为 fun interface 可支持 SAM 转换(直接传入 Lambda)。

kotlin
fun interface ClickListener {
    fun onClick(pos: Int)
}

// 使用
val listener = ClickListener { pos -> println("Clicked $pos") }