希望帮你在Kotlin进阶路上少走弯路,在技术上稳步提升。当然,由于个人知识储备有限,笔记中难免存在疏漏或表述不当的地方,也非常欢迎大家提出宝贵意见,一起交流进步。 ------ Android_小雨
整体目录:Kotlin 进阶不迷路:41 个核心知识点,构建完整知识体系
一、前言
在面向对象编程(OOP)的世界里,我们经常会遇到一种情况:我们能够清晰地描述一类事物的共同特征,但对于某些具体的行为,却无法在父类中给出确切的定义。
1.1 抽象类的核心价值
抽象类(Abstract Class)的核心价值在于定义共性模板 并约束子类实现。 它像是一个半成品的蓝图,规定了建筑的基本结构(共性),但留下了某些具体的装修风格(个性)给子类去完成。
1.2 Kotlin 抽象类的设计初衷
Kotlin 设计抽象类的初衷,是在代码复用 与子类规范之间寻找平衡。
- 复用:将通用的状态和逻辑提取到父类,避免重复代码。
- 规范:通过抽象方法强制子类必须实现特定逻辑,保证业务流程的完整性。
1.3 本文核心内容预告
本文将带您深入 Kotlin 抽象类的世界,路线如下:
- 基础定义:语法与混合成员结构。
- 核心特性:不可实例化、构造函数支持与强制约束。
- 继承实战:4 种实现抽象成员的高级技巧。
- 实战场景:BaseActivity、业务模板与共性逻辑封装。
二、抽象类基础:定义与语法规范
2.1 基本语法
在 Kotlin 中,使用 abstract 关键字来修饰一个类,使其成为抽象类。
kotlin
abstract class BaseTemplate {
// ...
}
2.2 抽象成员
使用 abstract 修饰的属性或方法,称为抽象成员。它们没有具体的实现(没有方法体,也没有属性初始化器),仅定义了"签名"。
2.3 非抽象成员
抽象类中可以包含普通(非抽象)的成员。这些成员可以有具体的实现,供子类直接复用。这是抽象类区别于接口的重要特征之一。
2.4 基础示例(定义抽象基类)
让我们看一个包含所有成员类型的完整示例:
kotlin
abstract class Animal(
open val name: String // 构造函数中的属性
) {
// 2.3 非抽象成员:有状态,有实现
val creationTime = System.currentTimeMillis()
fun sleep() {
println("$name 正在睡觉")
}
// 2.2 抽象成员:无状态,无实现,强制子类定义
abstract val species: String
abstract fun makeSound()
// open 成员:有实现,但允许子类重写
open fun move() {
println("移动中...")
}
}
三、抽象类的核心特性
根据 Kotlin 官方文档,抽象类有以下核心法则:
3.1 不可直接实例化
抽象类是"半成品",因此你不能直接创建抽象类的实例。
kotlin
fun main() {
// ❌ 编译错误:Cannot create an instance of an abstract class
// val animal = Animal("Test")
}
3.2 子类的强制约束
如果一个非抽象类继承了抽象类,它必须实现(Override)父类中所有的抽象成员。这是抽象类的核心契约精神。
3.3 支持构造函数
与接口不同,抽象类可以拥有构造函数(主构造函数或次构造函数)和 init 代码块,用于初始化抽象类中的状态(State)。
3.4 兼容继承规则
- 抽象成员 :隐含
open,必须被重写。 - 非抽象成员 :默认为
final(不可重写)。如果希望子类能重写非抽象成员,必须显式加上open关键字。
四、抽象类的继承与实现步骤
4.1 子类继承抽象类
使用冒号 : 进行继承,并必须调用父类的构造函数。
4.2 实现抽象属性 / 方法
这是进阶的关键点。在 Kotlin 中,实现抽象成员有 4 种官方认可的合法方式:
kotlin
abstract class Base {
abstract val size: Int
}
class Concrete : Base() {
// 方式 1:直接赋值(最常见,生成 backing field 占用内存)
override val size: Int = 100
// 方式 2:自定义 Getter(推荐用于计算属性,不占内存)
// override val size: Int get() = 100
// 方式 3:逻辑计算
// override val size: Int get() = if (System.currentTimeMillis() > 0) 100 else 0
// 方式 4:var + setter(少见但合法)
// override var size: Int = 100
}
4.3 重写非抽象成员
子类可以重写父类中标记为 open 的非抽象成员,并通过 super 关键字调用父类逻辑。
4.4 完整实战示例
我们将 Shape 抽象类落地为具体的 Circle。
kotlin
// 抽象父类
abstract class Shape(val color: String) {
abstract fun calculateArea(): Double
open fun printInfo() {
println("I am a $color shape.")
}
}
// 子类实现
class Circle(color: String, val radius: Double) : Shape(color) {
// 必须实现抽象方法
override fun calculateArea(): Double = Math.PI * radius * radius
// 选择性重写 open 方法
override fun printInfo() {
super.printInfo() // 复用父类逻辑
println("Type: Circle, Radius: $radius")
}
}
五、抽象类的典型应用场景
5.1 框架基础模板 (Android BaseActivity)
利用抽象类统一生命周期管理和初始化流程,这是 Android 开发中最经典的应用。
kotlin
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getLayoutId()) // 模板逻辑:设置布局
initViews() // 模板逻辑:初始化视图
initData() // 模板逻辑:加载数据
}
// 强制子类提供布局 ID
abstract fun getLayoutId(): Int
// 强制子类实现初始化逻辑
abstract fun initViews()
abstract fun initData()
}
5.2 业务规范定义 (支付策略)
强制子类实现核心业务逻辑,确保业务完整性。
kotlin
abstract class PaymentProcessor {
// 核心支付逻辑,强制子类实现(支付宝、微信、银行卡逻辑不同)
abstract fun pay(amount: Double)
// 公共逻辑:支付前检查(可复用,也可重写)
open fun checkRisk(): Boolean {
println("执行通用风控检查...")
return true
}
}
5.3 共性逻辑封装 (Template Method 模式)
在 ViewModel 或 Presenter 层,封装数据加载的"骨架"。
kotlin
abstract class BaseViewModel {
// 模板方法:定义加载数据的标准流程
fun loadData() {
showLoading()
try {
val data = fetchData() // 具体获取数据的逻辑由子类决定
showSuccess(data)
} catch (e: Exception) {
showError(e)
}
}
abstract fun fetchData(): Any
// ... 省略 showLoading 等通用实现
}
六、抽象类的使用边界与注意事项
6.1 与普通类的核心区别
| 特性 | 普通类 (Class) | 抽象类 (Abstract Class) |
|---|---|---|
| 实例化 | 可以直接创建对象 | 不可实例化 |
| 成员修饰 | 默认为 final | 默认为 open (抽象成员),支持 abstract 修饰 |
| 用途 | 具体业务实现 | 继承体系的基石/模板 |
6.2 与接口的初步区分
这是架构选型时的关键决策点:
| 特性 | 抽象类 | 接口 (Interface) |
|---|---|---|
| 状态 (State) | 支持(有 backing field) | 不支持(无状态) |
| 构造函数 | 支持 | 不支持 |
| 多继承 | 不支持(单继承) | 支持 |
| 设计意图 | Is-A (它是...)、模板复用 | Can-Do (它能...)、行为契约 |
6.3 避坑点 (常见编译错误)
| 错误信息 | 原因与解法 |
|---|---|
Abstract member not implemented |
子类忘记实现抽象方法。解法:添加 override 实现。 |
Property must be initialized or be abstract |
属性没赋值也没加 abstract。解法:初始化或声明为 abstract。 |
This type is final |
试图继承普通类。解法:父类加 open 或 abstract。 |
七、总结与最佳实践
7.1 核心知识点回顾
- 关键字 :
abstract。 - 三大特征:不可实例化、包含抽象成员、强制子类实现。
- 继承规则:单继承,抽象成员必须 override,非抽象成员需 open 才可 override。
7.2 适用场景
当多个类之间存在明显的家族相似性 (Is-A 关系),且你需要复用代码 (状态/具体方法)同时又需要强制约束某些行为时,抽象类是最佳选择。
7.3 实践建议
- 多组合,少继承:抽象类虽好,但继承耦合度高。仅在确实需要模板复用时使用。
- 命名规范 :通常以
Base开头(如BaseActivity)或直接使用名词(如Shape),清晰表达其作为基类的身份。