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