接口和抽象类的基本定义
Kotlin 作为现代 Android 开发的主流语言,提供了接口(interface)和抽象类(abstract class)两种机制来实现代码复用和多态。虽然它们都能定义方法模板,但适用场景并不相同。
接口用于声明行为规范,所有方法默认是公开的抽象方法,也可以包含带实现的方法。抽象类则更像一个“半成品”类,可以包含抽象方法、具体实现,还能保存状态(即成员变量)。
接口的写法示例
比如我们想让多个类都具备“可点击”的能力:
interface Clickable {
fun onClick()
fun onLongClick(): Boolean {
return false
}
}这里 onClick 是抽象方法,子类必须实现;而 onLongClick 提供了默认实现,子类可以选择性重写。
抽象类的使用场景
假设我们开发一个天气应用,不同城市的天气逻辑有共性也有差异。这时候用抽象类就更合适:
abstract class WeatherCalculator {
protected var baseTemp = 20
abstract fun calculateToday(): Double
open fun calculateWeekAverage(): Double {
return baseTemp + 1.5
}
}子类继承时可以直接使用 baseTemp,也能选择是否重写 calculateWeekAverage。这种带状态和部分实现的结构,接口无法胜任。
关键区别点
接口不能保存状态,也就是不能有非 const 的属性;抽象类可以。接口支持多实现,一个类能实现多个接口;但只能继承一个抽象类。这就像一个人可以同时“会游泳”“会开车”,但出身家庭只能有一个。
从 Kotlin 1.2 开始,接口允许有默认实现,这让它比 Java 的接口更强大,也更容易和抽象类混淆。但在需要共享字段或构造逻辑时,抽象类仍是唯一选择。
实际项目怎么选
在写一个登录模块时,如果只是定义“登录”“登出”这些动作,用接口更灵活:
interface AuthProvider {
fun login(username: String, password: String): Boolean
fun logout()
}但如果要统一处理 token 刷新、请求头添加等逻辑,用抽象类封装公共代码会减少重复:
abstract class BaseApiService {
protected val headers = mutableMapOf<String, String>()
init {
headers["Content-Type"] = "application/json"
}
abstract fun fetchData(): String
}多数情况下,优先考虑接口,只有当需要共享状态或复杂初始化时才用抽象类。这样代码更干净,也更容易测试和扩展。