第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 的默认行为正好相反,我刚开始也踩过这个坑。
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 不同
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 端调用更清晰。
知识体系总览
下面这张图帮你梳理本章的核心知识点:
总结一下
这一章我们覆盖了 Kotlin 面向对象编程的核心内容。从最基础的类与对象,到主次构造函数,再到继承、接口、数据类、密封类,以及 object 关键字。每一个知识点都是你在日常 Android 开发中会频繁用到的。
我个人觉得,Kotlin 的设计哲学就是「少写样板代码,多表达业务逻辑」。你写多了就会发现,很多 Java 里需要手动处理的事情,Kotlin 都帮你自动搞定了。
好了,这一章就到这里。代码多敲几遍,尤其是密封类和伴生对象,刚开始可能会觉得别扭,但用顺了之后你会爱上它们的。