第1章:面向对象编程——类与对象、构造函数、继承与接口、数据类、密封类、object关键字

各位同学,欢迎来到 Kotlin 面向对象编程的世界。

说实话,我刚开始从 Java 转到 Kotlin 时,最直观的感受就是——这语言太懂开发者了。尤其是面向对象这部分,Kotlin 做了大量简化,但同时也引入了一些全新的概念。今天这一章,我会带你把这些核心知识点一个一个啃下来。

1. 类与对象:一切的基础

在 Kotlin 中,定义一个类非常简单。用 class 关键字就行。

class Person {
    var name: String = ""
    var age: Int = 0
}

然后创建对象:

val person = Person()
person.name = "张三"
person.age = 25

嗯,这里要注意:Kotlin 默认的类都是 public final 的。如果你想被继承,必须显式加上 open 关键字。这一点和 Java 的默认行为正好相反,我刚开始也踩过这个坑。

💡 我的习惯: 我一般会先写一个不带任何修饰的类,等确实需要继承时再加 open。这样能避免滥用继承。

2. 构造函数:主构造 vs 次构造

Kotlin 的构造函数分两种:主构造函数和次构造函数。

主构造函数直接写在类名后面:

class Person(val name: String, var age: Int)

你看,一行代码就搞定了属性声明和初始化。这在 Java 里得写多少 getter/setter 啊。

如果你需要额外的初始化逻辑,可以用 init 块:

class Person(val name: String, var age: Int) {
    init {
        require(age >= 0) { "年龄不能为负数" }
        println("创建了一个人:$name,$age 岁")
    }
}

次构造函数constructor 关键字定义,必须直接或间接委托给主构造函数:

class Person(val name: String, var age: Int) {
    constructor(name: String) : this(name, 0) {
        println("使用次构造函数,年龄默认为0")
    }
}
⚠️ 我曾经踩过的坑: 次构造函数中不能直接调用父类构造函数,必须通过主构造函数中转。如果你忘了加 this() 委托,编译器会直接报错。

3. 继承与接口

Kotlin 的继承用冒号 : 表示。父类必须加 open,方法也必须加 open 才能被重写。

open class Animal(val name: String) {
    open fun speak() {
        println("$name 发出声音")
    }
}

class Dog(name: String) : Animal(name) {
    override fun speak() {
        println("$name 汪汪叫")
    }
}

接口用 interface 定义,Kotlin 的接口可以有默认实现:

interface Flyable {
    fun fly() {
        println("飞行中...")
    }
}

interface Swimmable {
    fun swim()
}

class Duck(name: String) : Animal(name), Flyable, Swimmable {
    override fun swim() {
        println("$name 在游泳")
    }
}

你想想看,一个类可以实现多个接口,但只能继承一个父类。这个设计其实很合理——接口定义能力,类定义本质。

4. 数据类:专门用来装数据的

数据类用 data 关键字修饰。它会自动生成 equals()hashCode()toString()copy() 等方法。

data class User(val id: Long, val name: String, val email: String)

就这么一行,你就能直接比较两个 User 对象是否相等,或者用 copy() 创建一个只改某个字段的新对象:

val user1 = User(1, "张三", "zhangsan@example.com")
val user2 = user1.copy(email = "newemail@example.com")
println(user1 == user2) // false,因为 email 不同
📌 我在项目中常用的场景: 数据类特别适合用来做网络请求的响应体、数据库实体、或者 ViewModel 中的 UI 状态。配合 Kotlin 的不可变性,能写出非常安全的代码。

5. 密封类:限制继承的利器

密封类用 sealed 关键字修饰。它限制了子类只能在同一个文件中定义。说白了,就是告诉编译器:「这个类的所有可能子类我都列在这里了,你别担心有遗漏」。

sealed class Result {
    data class Success(val data: String) : Result()
    data class Error(val message: String) : Result()
    object Loading : Result()
}

配合 when 表达式使用,编译器能帮你检查是否覆盖了所有分支:

fun handleResult(result: Result) {
    when (result) {
        is Result.Success -> println("成功:${result.data}")
        is Result.Error -> println("失败:${result.message}")
        Result.Loading -> println("加载中...")
    }
    // 不需要 else 分支,因为所有情况都覆盖了
}

我个人非常喜欢密封类。它比枚举更灵活,比普通继承更安全。在 Android 开发中,我经常用它来表示页面状态、网络请求结果、或者导航事件。

6. object 关键字:单例与伴生对象

Kotlin 用 object 关键字来创建单例。不需要像 Java 那样写双重检查锁,一行搞定:

object NetworkConfig {
    val baseUrl = "https://api.example.com"
    val timeout = 30_000L
}

使用时直接 NetworkConfig.baseUrl 就行。

伴生对象companion object 定义,相当于 Java 中的静态成员:

class MyApp {
    companion object {
        const val TAG = "MyApp"
        fun create(): MyApp = MyApp()
    }
}

// 调用
MyApp.TAG
MyApp.create()
💡 我的建议: 伴生对象的名字可以省略,默认叫 Companion。但如果你在 Java 代码中调用,最好给它起个名字,比如 companion object Factory,这样 Java 端调用更清晰。

知识体系总览

下面这张图帮你梳理本章的核心知识点:

面向对象编程 类与对象 构造函数 继承与接口 数据类 data 密封类 sealed object 关键字 主构造 次构造 单例 伴生对象 Kotlin 面向对象:简洁、安全、表达力强

总结一下

这一章我们覆盖了 Kotlin 面向对象编程的核心内容。从最基础的类与对象,到主次构造函数,再到继承、接口、数据类、密封类,以及 object 关键字。每一个知识点都是你在日常 Android 开发中会频繁用到的。

我个人觉得,Kotlin 的设计哲学就是「少写样板代码,多表达业务逻辑」。你写多了就会发现,很多 Java 里需要手动处理的事情,Kotlin 都帮你自动搞定了。

好了,这一章就到这里。代码多敲几遍,尤其是密封类和伴生对象,刚开始可能会觉得别扭,但用顺了之后你会爱上它们的。


公众号:蓝海资料掘金营,微信deep3321