类与对象
本教程共 100 篇 · 第 13 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
13. 类与对象
本节目标:学会用 Kotlin 定义类、写构造函数、用属性、做继承、实现接口和抽象类,搞懂 Kotlin 面向对象跟 Java 的区别。
定义类
class Person {
var name: String = ""
var age: Int = 0
}
Kotlin 类默认是 final 的(不能被继承),要能继承得加 open:
open class Animal
class Dog : Animal() // 继承要加 (),调用父类构造
主构造函数
Kotlin 把构造函数写在类名后面,叫主构造函数:
class Person(val name: String, var age: Int)
参数加 val/var 就成了类的属性,自动生成 getter(var 还有 setter)。没加 val/var 只是构造参数,不是属性。
val p = Person("张三", 18)
println(p.name) // 张三
p.age = 19 // var 可改
// p.name = "李四" // val 只读,报错
Tip
- 这一行代码相当于 Java 几十行:字段、构造函数、getter、setter。Kotlin 简洁的典型。
init 块
主构造函数不能有代码体,初始化逻辑放 init 块:
class User(val name: String, val age: Int) {
init {
require(age >= 0) { "年龄不能为负" }
println("创建用户:$name")
}
}
属性初始化和 init 块按出现顺序执行。可以有多个 init 块。
次构造函数
用 constructor 关键字,必须委托给主构造函数:
class User(val name: String, val age: Int) {
var email: String = ""
// 次构造函数,通过 this 委托主构造
constructor(name: String) : this(name, 0)
constructor(name: String, age: Int, email: String) : this(name, age) {
this.email = email
}
}
Note
- 能用默认参数搞定的就别写次构造函数。Kotlin 鼓励用主构造 + 默认参数,少写一堆重载。
属性
class Circle {
var radius: Double = 1.0
set(value) {
field = value // field 是幕后字段
recalculate()
}
val area: Double // 只读属性,自定义 getter
get() = Math.PI * radius * radius
private fun recalculate() { /* ... */ }
}
field是 Kotlin 给的幕后字段标识符,只在 getter/setter 内部用。- 自定义 getter 的属性可以没有幕后字段。
继承
open class Animal(val name: String) {
open fun sound() {
println("$name 发出声音")
}
}
class Dog(name: String) : Animal(name) {
override fun sound() { // 重写要 override
println("$name 汪汪叫")
}
}
要点:
- 父类要
open才能被继承。 - 要重写的方法也要
open,子类用override。 override的方法默认还能被进一步重写,想锁住加final override。- 调用父类实现用
super。
抽象类
abstract class Shape {
abstract fun area(): Double // 抽象方法,无实现
fun describe() = "面积是 ${area()}" // 普通方法
}
class Circle(val radius: Double) : Shape() {
override fun area() = Math.PI * radius * radius
}
抽象类不能实例化,抽象方法子类必须实现。抽象类天然是 open 的,不用加。
接口
Kotlin 接口比 Java 8 的接口更强,能有默认实现:
interface Clickable {
fun click() // 抽象方法
fun showOff() = println("我能被点击") // 默认实现
}
class Button : Clickable {
override fun click() {
println("按钮被点击")
}
// showOff 不重写就用默认实现
}
实现多个接口
接口可以多实现,类继承只能单继承:
interface Focusable {
fun focus()
fun showOff() = println("我能获得焦点")
}
class Button : Clickable, Focusable {
override fun click() { /* ... */ }
override fun focus() { /* ... */ }
// 两个接口都有 showOff,必须重写消歧义
override fun showOff() {
super<Clickable>.showOff()
super<Focusable>.showOff()
}
}
Note
- 接口里的属性不能有幕后字段(不能存状态),只能是抽象的或有 getter 的。需要状态用抽象类。
interface vs abstract 怎么选
- 接口:定义「能力」,类可以多实现。比如
Clickable、Drawable、Comparable。 - 抽象类:定义「是什么」,共享代码和状态。比如
BaseActivity、BaseViewModel。
Android 里常见模式:抽象类做基类(共享逻辑),接口做能力契约。
可见性修饰符
Kotlin 四种可见性:
| 修饰符 | 含义 |
|---|---|
public(默认) | 所有地方可见 |
private | 同一文件内可见(顶层)或同一类内(成员) |
internal | 同一模块内可见 |
protected | 同一类及子类可见(不能用于顶层声明) |
Tip
internal是 Kotlin 特有的,模块内可见。Android 多模块项目里很有用,能控制 API 不暴露给其他模块。
object:单例
Kotlin 用 object 一行声明单例:
object AppConfig {
val version = "1.0"
fun init() { /* ... */ }
}
// 用法
AppConfig.init()
println(AppConfig.version)
底层编译成饿汉式单例,线程安全。比 Java 手写单例简洁太多。
伴生对象
Kotlin 没有 static 关键字。类级别的常量和工厂方法放在伴生对象(companion object)里:
class User(val name: String) {
companion object {
const val DEFAULT_NAME = "匿名"
fun create(): User = User(DEFAULT_NAME)
}
}
// 用法
User.DEFAULT_NAME
User.create()
const val 是编译期常量,比普通 val 更高效。伴生对象里能实现接口、做工厂。
Android 实战示例
// 抽象 Activity 基类
abstract class BaseActivity : ComponentActivity() {
protected abstract val layoutRes: Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupContent()
}
protected abstract fun setupContent()
}
// 接口定义能力
interface Refreshable {
fun refresh()
}
// 数据模型
class UserSession(val userId: String) {
var token: String? = null
private set
companion object {
const val KEY_USER_ID = "user_id"
}
}
小结
主构造写在类名后,参数加 val/var 成属性,初始化放 init 块。继承要 open,重写要 override。抽象类共享代码,接口定义能力可多实现。object 做单例,伴生对象放类级常量和工厂。可见性多了 internal。下一节学数据类与密封类。