跳到主要内容

Kotlin 面向对象

面向对象编程(OOP)是现代软件开发的核心范式。Kotlin 作为一门现代语言,在支持传统 OOP 特性的同时,引入了许多改进和简化。本章将深入介绍类、对象、继承、接口等核心概念,并探讨 Kotlin 特有的面向对象特性。

类与对象

类的定义

类是对象的蓝图,定义了对象的属性和行为。Kotlin 的类定义语法简洁明了:

// 基本类定义
class Person {
// 属性(成员变量)
var name: String = ""
var age: Int = 0

// 方法(成员函数)
fun sayHello() {
println("你好,我是 $name,今年 $age 岁")
}

fun isAdult(): Boolean = age >= 18
}

// 使用类创建对象
fun main() {
// 创建实例
val person = Person()

// 设置属性
person.name = "张三"
person.age = 25

// 调用方法
person.sayHello() // 你好,我是 张三,今年 25 岁
println(person.isAdult()) // true
}

主构造函数

Kotlin 将构造函数分为主构造函数次构造函数。主构造函数在类头中声明,是最简洁的构造方式:

// 主构造函数:在类名后声明
class Person(val name: String, var age: Int) {
// val/var 参数自动成为属性
fun introduce() = "我是 $name$age 岁"
}

fun main() {
val person = Person("李四", 30)
println(person.introduce()) // 我是 李四,30 岁
person.age = 31 // var 属性可修改
// person.name = "王五" // val 属性不可修改
}

主构造函数参数的 valvar 区别

  • val:创建只读属性,只能在构造时赋值
  • var:创建可变属性,可以后续修改
  • 无修饰符:仅作为构造参数,不成为属性
class Example(
val readOnly: String, // 只读属性
var mutable: Int, // 可变属性
temporary: String // 仅构造参数,不成为属性
) {
fun print() {
println(readOnly) // 可访问
println(mutable) // 可访问
// println(temporary) // 编译错误:无法访问
}
}

init 初始化块

init 块在类实例化时执行,用于执行初始化逻辑或验证参数:

class Person(val name: String, val age: Int) {
// 初始化块:在主构造函数后执行
init {
// 参数验证
require(name.isNotEmpty()) { "姓名不能为空" }
require(age >= 0) { "年龄不能为负数" }
require(age <= 150) { "年龄不合理" }
println("创建 Person: $name, $age 岁")
}

// 可以有多个 init 块,按声明顺序执行
init {
println("初始化完成")
}
}

fun main() {
val person = Person("张三", 25)
// Person("张三", -1) // 抛出 IllegalArgumentException
}

次构造函数

使用 constructor 关键字定义次构造函数,必须委托给主构造函数:

class User(val id: Int, val name: String, val email: String) {
// 次构造函数:必须调用主构造函数
constructor(id: Int, name: String) : this(id, name, "$name@example.com") {
println("使用默认邮箱创建用户")
}

// 另一个次构造函数
constructor(id: Int) : this(id, "User$id", "user$id@example.com") {
println("使用默认信息创建用户")
}

fun info() = "User($id, $name, $email)"
}

fun main() {
val u1 = User(1, "张三", "[email protected]")
println(u1.info()) // User(1, 张三, [email protected])

val u2 = User(2, "李四")
println(u2.info()) // User(2, 李四, 李四@example.com)

val u3 = User(3)
println(u3.info()) // User(3, User3, [email protected])
}

设计建议:优先使用默认参数而非次构造函数:

// 推荐:使用默认参数
class UserBetter(
val id: Int,
val name: String = "User$id",
val email: String = "$name@example.com"
)

fun main() {
val u1 = UserBetter(1, "张三", "[email protected]")
val u2 = UserBetter(2, "李四")
val u3 = UserBetter(3)
}

属性

属性的基本概念

Kotlin 的属性远比 Java 的字段强大,它自动生成 getter 和 setter(对于 var):

class Person {
// var 属性:自动生成 getter 和 setter
var name: String = "Unknown"

// val 属性:只生成 getter
val id: Int = (1..10000).random()
}

fun main() {
val person = Person()
println(person.name) // 调用 getter
person.name = "张三" // 调用 setter
println(person.id) // 调用 getter
// person.id = 2 // 编译错误:val 不可重新赋值
}

自定义 getter 和 setter

class Rectangle(val width: Int, val height: Int) {
// 计算属性:自定义 getter
val area: Int
get() = width * height

// 带逻辑的 setter
var description: String = "矩形"
get() = "$field (宽: $width, 高: $height)"
set(value) {
field = value.uppercase() // field 是幕后字段
}

// 可观察属性
var count: Int = 0
set(value) {
println("count 从 $field 变为 $value")
field = value
}
}

fun main() {
val rect = Rectangle(10, 20)
println("面积: ${rect.area}") // 200
println(rect.description) // 矩形 (宽: 10, 高: 20)

rect.count = 5 // count 从 0 变为 5
rect.count = 10 // count 从 5 变为 10
}

幕后字段 field:在 getter/setter 中,field 标识符引用属性的幕后存储。只在需要时生成,避免递归调用。

class Example {
// 正确:使用 field
var value: Int = 0
set(newValue) {
if (newValue >= 0) field = newValue
}

// 错误:会导致无限递归
// var wrong: Int = 0
// set(newValue) {
// wrong = newValue // 调用 setter,无限循环!
// }
}

延迟初始化

当属性无法在构造时初始化时,可以使用延迟初始化:

class Service {
// lateinit:用于 var,稍后初始化
lateinit var database: Database

fun init() {
database = Database.connect()
}

fun query(): String {
// 检查是否已初始化
if (::database.isInitialized) {
return database.query()
}
return "数据库未初始化"
}
}

class Database {
companion object {
fun connect() = Database()
}
fun query() = "查询结果"
}

// lazy:用于 val,首次访问时初始化
class Config {
// 惰性初始化:线程安全
val settings: Map<String, String> by lazy {
println("加载配置...")
mapOf("host" to "localhost", "port" to "8080")
}

// 惰性初始化:非线程安全(性能更好)
val cache: MutableList<String> by lazy(LazyThreadSafetyMode.NONE) {
mutableListOf()
}
}

fun main() {
val config = Config()
println("创建 Config 完成")
println(config.settings) // 此时才初始化
}

lateinit vs lazy 选择

特性lateinitlazy
适用类型varval
初始化时机手动控制首次访问
线程安全无保证默认安全
可空性非空任意
检查初始化::property.isInitialized自动

可见性修饰符

Kotlin 提供四种可见性修饰符,控制类、属性、方法的访问级别:

// 可见性修饰符示例
class VisibilityExample {
// public(默认):随处可见
var publicProp: String = "public"

// private:仅类内可见
private var privateProp: String = "private"

// protected:类内及子类可见
protected var protectedProp: String = "protected"

// internal:模块内可见
internal var internalProp: String = "internal"

// 私有方法
private fun privateMethod() = "私有方法"

// 公开方法可以访问私有成员
fun accessPrivate() = privateProp

// 嵌套类可以访问外部类的私有成员
class Nested {
fun accessOuter(outer: VisibilityExample) {
// println(outer.privateProp) // 编译错误
println(outer.publicProp)
}
}
}

// 继承示例
class Child : VisibilityExample() {
fun accessProtected() {
println(protectedProp) // 可以访问
// println(privateProp) // 编译错误
}
}

// 顶层可见性
private class PrivateClass // 仅本文件可见
internal class InternalClass // 模块内可见

可见性总结

修饰符类成员顶层声明
public随处可见随处可见
private仅类内可见仅文件内可见
protected类及子类可见不适用
internal模块内可见模块内可见

继承

类的继承

Kotlin 的类默认是 final 的,必须使用 open 关键字才能被继承:

// 基类必须用 open 修饰
open class Animal(val name: String) {
// 可被覆盖的方法也要用 open
open fun makeSound() {
println("$name 发出声音")
}

// 不可覆盖的方法
fun sleep() {
println("$name 正在睡觉")
}
}

// 子类继承
class Dog(name: String, val breed: String) : Animal(name) {
// 覆盖方法
override fun makeSound() {
println("$name ($breed) 汪汪叫")
}

// 子类特有方法
fun fetch() {
println("$name 去捡球")
}
}

class Cat(name: String) : Animal(name) {
override fun makeSound() {
println("$name 喵喵叫")
}
}

fun main() {
val dog = Dog("旺财", "拉布拉多")
dog.makeSound() // 旺财 (拉布拉多) 汪汪叫
dog.sleep() // 旺财 正在睡觉

// 多态
val animals: List<Animal> = listOf(Dog("小黑", "德牧"), Cat("小花"))
animals.forEach { it.makeSound() }
}

调用父类方法

使用 super 关键字调用父类方法:

open class Vehicle(val brand: String) {
open fun start() {
println("$brand 启动")
}
}

class Car(brand: String, val model: String) : Vehicle(brand) {
override fun start() {
super.start() // 调用父类方法
println("$brand $model 准备就绪")
}

fun showInfo() {
println("品牌: ${super.brand}") // 访问父类属性
}
}

fun main() {
val car = Car("Toyota", "Camry")
car.start()
// Toyota 启动
// Toyota Camry 准备就绪
}

多态与类型转换

open class Shape {
open fun draw() = "绘制形状"
open fun area(): Double = 0.0
}

class Circle(val radius: Double) : Shape() {
override fun draw() = "绘制圆形(半径 $radius)"
override fun area() = Math.PI * radius * radius

fun circumference() = 2 * Math.PI * radius
}

class Rectangle(val width: Double, val height: Double) : Shape() {
override fun draw() = "绘制矩形(${width}x${height})"
override fun area() = width * height
}

fun main() {
// 多态:父类引用指向子类对象
val shapes: List<Shape> = listOf(
Circle(5.0),
Rectangle(4.0, 3.0),
Circle(2.5)
)

shapes.forEach { shape ->
println("${shape.draw()},面积: ${shape.area()}")

// 智能类型转换
if (shape is Circle) {
println(" 周长: ${shape.circumference()}")
}
}

// 安全类型转换
val circle = shapes[0] as? Circle
println("是圆形吗?${circle != null}")

// 不安全转换(可能抛出 ClassCastException)
// val rect = shapes[0] as Rectangle // 运行时错误
}

接口

接口定义了行为的契约,Kotlin 接口可以包含抽象方法、默认实现和属性。

接口定义与实现

// 定义接口
interface Drawable {
// 抽象属性
val color: String

// 抽象方法
fun draw()

// 默认实现
fun description() = "这是一个可绘制的对象,颜色: $color"

// 可以有默认实现的方法
fun reset() {
println("重置绘制状态")
}
}

interface Clickable {
fun click()
fun doubleClick() {
println("双击")
}
}

// 实现接口
class Button(override val color: String, val text: String) : Drawable, Clickable {
override fun draw() {
println("绘制 $color 按钮: $text")
}

override fun click() {
println("点击按钮: $text")
}

// 可以覆盖默认实现
override fun doubleClick() {
println("按钮 $text 被双击")
}
}

fun main() {
val button = Button("蓝色", "确定")
button.draw() // 绘制 蓝色 按钮: 确定
button.click() // 点击按钮: 确定
button.description() // 这是一个可绘制的对象,颜色: 蓝色
}

接口继承与冲突解决

interface A {
fun foo() { println("A.foo") }
fun bar()
}

interface B {
fun foo() { println("B.foo") }
fun bar() { println("B.bar") }
}

// 实现多个接口
class C : A, B {
// 必须覆盖冲突的方法
override fun foo() {
super<A>.foo() // 调用 A 的实现
super<B>.foo() // 调用 B 的实现
println("C.foo")
}

override fun bar() {
super<B>.bar() // 调用 B 的实现
}
}

fun main() {
val c = C()
c.foo()
// A.foo
// B.foo
// C.foo
}

接口 vs 抽象类

特性接口抽象类
多继承可以实现多个只能继承一个
状态不能有状态可以有成员变量
构造函数
属性只能是抽象或有访问器可以有具体属性
使用场景定义行为契约共享实现和状态
// 抽象类:共享状态和实现
abstract class BaseViewModel {
protected var isLoading = false

abstract fun load()

fun showLoading() {
isLoading = true
println("显示加载中...")
}

fun hideLoading() {
isLoading = false
println("隐藏加载")
}
}

// 接口:定义能力
interface Refreshable {
fun refresh()
}

interface Searchable {
fun search(query: String)
}

// 结合使用
class UserViewModel : BaseViewModel(), Refreshable, Searchable {
override fun load() {
showLoading()
println("加载用户数据")
hideLoading()
}

override fun refresh() {
println("刷新用户数据")
}

override fun search(query: String) {
println("搜索用户: $query")
}
}

数据类

数据类专门用于存储数据,自动生成 equals()hashCode()toString()copy() 等方法:

// 定义数据类
data class User(
val id: Int,
val name: String,
val email: String,
val age: Int = 0 // 默认参数
)

fun main() {
val u1 = User(1, "张三", "[email protected]", 25)
val u2 = User(1, "张三", "[email protected]", 25)
val u3 = User(2, "李四", "[email protected]")

// 自动生成的 toString()
println(u1) // User(id=1, name=张三, [email protected], age=25)

// 自动生成的 equals():比较所有属性
println(u1 == u2) // true
println(u1 == u3) // false

// 自动生成的 hashCode()
println(u1.hashCode() == u2.hashCode()) // true

// copy():复制并修改部分属性
val u4 = u1.copy(name = "王五")
println(u4) // User(id=1, name=王五, [email protected], age=25)

// 解构声明
val (id, name, email, age) = u1
println("ID: $id, 姓名: $name, 邮箱: $email, 年龄: $age")

// 部分解构
val (userId, userName) = u1
println("$userId: $userName")
}

数据类的限制

// 数据类必须满足:
// 1. 主构造函数至少有一个参数
// 2. 主构造函数参数必须标记为 val 或 var
// 3. 不能是 abstract、open、sealed、inner

// 错误示例:
// data class Empty() // 可以,但没有意义

// 数据类可以有额外的方法和属性
data class Product(
val id: Int,
val name: String,
val price: Double
) {
// 计算属性
val formattedPrice: String
get() = ${String.format("%.2f", price)}"

// 额外方法
fun isExpensive() = price > 1000

// 可以覆盖自动生成的方法
override fun toString(): String {
return "商品[$id]: $name - $formattedPrice"
}
}

fun main() {
val product = Product(1, "手机", 2999.0)
println(product.formattedPrice) // ¥2999.00
println(product.isExpensive()) // true
println(product) // 商品[1]: 手机 - ¥2999.00
}

密封类

密封类限制继承层次,只能在同一文件中定义子类。配合 when 表达式实现穷尽检查:

// 密封类:受限的类层次结构
sealed class Result {
// 子类可以是 data class、object 或普通 class
data class Success(val data: String) : Result()
data class Error(val code: Int, val message: String) : Result()
object Loading : Result()
object Empty : Result()
}

// 处理结果(when 必须穷尽所有情况)
fun handleResult(result: Result): String = when (result) {
is Result.Success -> "成功: ${result.data}"
is Result.Error -> "错误 ${result.code}: ${result.message}"
Result.Loading -> "加载中..."
Result.Empty -> "无数据"
// 不需要 else 分支,编译器确保覆盖所有情况
}

// 密封接口(Kotlin 1.5+)
sealed interface UiState {
object Idle : UiState
object Loading : UiState
data class Success(val data: Any) : UiState
data class Error(val throwable: Throwable) : UiState
}

fun main() {
val results = listOf(
Result.Success("用户数据"),
Result.Error(404, "未找到"),
Result.Loading,
Result.Empty
)

results.forEach { println(handleResult(it)) }
}

密封类的实际应用

// 网络请求状态
sealed class NetworkState<out T> {
object Idle : NetworkState<Nothing>()
object Loading : NetworkState<Nothing>()
data class Success<T>(val data: T) : NetworkState<T>()
data class Error(val exception: Throwable) : NetworkState<Nothing>()
}

// UI 事件
sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
data class Navigate(val route: String) : UiEvent()
data class ShowDialog(val title: String, val message: String) : UiEvent()
object PopBackStack : UiEvent()
}

// 支付方式
sealed class PaymentMethod {
data class CreditCard(val number: String, val cvv: String) : PaymentMethod()
data class PayPal(val email: String) : PaymentMethod()
object Cash : PaymentMethod()

fun process(amount: Double): String = when (this) {
is CreditCard -> "信用卡支付 $$amount"
is PayPal -> "PayPal 支付 $$amount"
Cash -> "现金支付 ¥$amount"
}
}

fun main() {
val payment = PaymentMethod.CreditCard("****1234", "123")
println(payment.process(99.99))
}

枚举类

枚举类定义一组命名的常量值:

// 基本枚举
enum class Direction {
NORTH, SOUTH, EAST, WEST
}

// 带属性的枚举
enum class Color(val rgb: Int, val chineseName: String) {
RED(0xFF0000, "红色"),
GREEN(0x00FF00, "绿色"),
BLUE(0x0000FF, "蓝色");

fun hex() = "#${Integer.toHexString(rgb)}"
}

// 带方法的枚举
enum class Planet(val mass: Double, val radius: Double) {
EARTH(5.976e24, 6.37814e3),
MARS(6.421e23, 3.3972e3),
JUPITER(1.9e27, 7.1492e4);

// 计算表面重力
fun surfaceGravity() = 6.67300E-11 * mass / (radius * radius)

fun surfaceWeight(otherMass: Double) = otherMass * surfaceGravity()

override fun toString() = name.lowercase().replaceFirstChar { it.uppercase() }
}

fun main() {
// 遍历枚举
Direction.entries.forEach { println(it) }

// 获取枚举值
val color = Color.RED
println("${color.chineseName}: ${color.hex()}") // 红色: #ff0000

// 枚举在 when 中使用
val dir = Direction.NORTH
val description = when (dir) {
Direction.NORTH -> "北方"
Direction.SOUTH -> "南方"
Direction.EAST -> "东方"
Direction.WEST -> "西方"
}
println(description)

// 行星计算
val earthWeight = 75.0
println("地球表面重力: ${Planet.EARTH.surfaceGravity()}")
println("火星上的重量: ${Planet.MARS.surfaceWeight(earthWeight)}")
}

枚举与接口

interface Printable {
fun print(): String
}

enum class Status : Printable {
PENDING {
override fun print() = "待处理"
},
PROCESSING {
override fun print() = "处理中"
},
COMPLETED {
override fun print() = "已完成"
},
FAILED {
override fun print() = "失败"
};

// 枚举常量可以覆盖该方法
override fun print() = name
}

fun main() {
Status.entries.forEach {
println("${it.name}: ${it.print()}")
}
}

对象声明与伴生对象

单例对象

使用 object 关键字声明单例,整个程序只有一个实例:

// 单例对象
object Database {
private val connection = mutableMapOf<String, String>()

val url: String = "jdbc:mysql://localhost:3306/mydb"

fun connect() {
println("连接数据库: $url")
}

fun query(sql: String): List<Map<String, Any>> {
println("执行查询: $sql")
return emptyList()
}

fun close() {
println("关闭连接")
}
}

fun main() {
// 直接使用,无需创建实例
Database.connect()
Database.query("SELECT * FROM users")

// 类似静态成员的访问方式
println(Database.url)
}

伴生对象

伴生对象是类内部的单例,可以访问类的私有成员,类似静态成员:

class User private constructor(val name: String) {
companion object {
private var count = 0

// 常量
const val TAG = "User"

// 工厂方法
fun create(name: String): User {
count++
return User(name)
}

fun createDefault(): User = User("Guest")

// 可以实现接口
fun count() = count
}

fun show() = "User: $name (总共创建 $count 个)"
}

fun main() {
// 通过伴生对象调用
val u1 = User.create("张三")
val u2 = User.createDefault()

println(u1.show()) // User: 张三 (总共创建 2 个)
println(User.TAG) // User
println("共创建 ${User.count()} 个用户")
}

伴生对象实现接口

interface Factory<T> {
fun create(): T
}

class Product(val id: Int, val name: String) {
companion object : Factory<Product> {
private var nextId = 0

override fun create(): Product {
return Product(nextId++, "Product-$nextId")
}

fun createWithName(name: String) = Product(nextId++, name)
}
}

fun main() {
// 作为接口使用
val factory: Factory<Product> = Product
val product = factory.create()
println(product)
}

嵌套类与内部类

嵌套类(静态内部类)

默认情况下,Kotlin 的嵌套类是静态的,不持有外部类引用:

class Outer {
private val outerValue = 10

// 嵌套类(默认 static)
class Nested {
fun nestedMethod() = "嵌套类方法"
// 不能访问 outerValue
}
}

fun main() {
// 直接创建嵌套类实例
val nested = Outer.Nested()
println(nested.nestedMethod())
}

内部类

使用 inner 关键字声明内部类,持有外部类引用:

class Outer {
private val outerValue = 10

fun showOuter() = "外部类: outerValue = $outerValue"

// 内部类:可以访问外部类成员
inner class Inner {
private val innerValue = 20

fun innerMethod() = "内部类: innerValue = $innerValue, outerValue = $outerValue"

fun accessOuter() = this@Outer.showOuter() // 访问外部类方法
}
}

fun main() {
val outer = Outer()
// 必须通过外部类实例创建内部类
val inner = outer.Inner()
println(inner.innerMethod())
println(inner.accessOuter())
}

匿名内部类

使用 object 表达式创建匿名内部类:

interface OnClickListener {
fun onClick()
fun onLongClick() {}
}

class Button {
fun setOnClickListener(listener: OnClickListener) {
listener.onClick()
}
}

fun main() {
val button = Button()

// 匿名内部类
button.setOnClickListener(object : OnClickListener {
override fun onClick() {
println("按钮被点击")
}

override fun onLongClick() {
println("按钮被长按")
}
})

// 使用 Lambda(如果接口是函数式接口)
// button.setOnClickListener { println("点击") }
}

类型别名

使用 typealias 为类型创建别名,提高代码可读性:

// 类型别名
typealias UserMap = Map<String, MutableList<User>>
typealias Predicate<T> = (T) -> Boolean
typealias ClickHandler = (Int, Int) -> Unit

// 扩展函数别名
typealias Validator = String.() -> Boolean

fun main() {
// 使用别名
val users: UserMap = mutableMapOf(
"admin" to mutableListOf(User(1, "管理员", "[email protected]"))
)

// 函数类型别名
val isAdult: Predicate<User> = { it.age >= 18 }
val handler: ClickHandler = { x, y -> println("点击 ($x, $y)") }

// 扩展函数别名使用
val isValidEmail: Validator = { contains("@") && contains(".") }
"[email protected]".isValidEmail()
}

设计模式实践

单例模式

Kotlin 的 object 声明天然实现单例模式:

// 方式一:object 声明(推荐)
object Singleton {
fun doSomething() = "单例操作"
}

// 方式二:伴生对象 + 私有构造函数
class SingletonPrivate private constructor() {
companion object {
val instance: SingletonPrivate by lazy { SingletonPrivate() }
}

fun doSomething() = "懒加载单例"
}

// 方式三:双重检查锁(多线程安全)
class SingletonThreadSafe private constructor() {
companion object {
@Volatile
private var instance: SingletonThreadSafe? = null

fun getInstance(): SingletonThreadSafe {
return instance ?: synchronized(this) {
instance ?: SingletonThreadSafe().also { instance = it }
}
}
}
}

工厂模式

// 工厂方法模式
interface Shape {
fun draw()
}

class Circle(val radius: Double) : Shape {
override fun draw() = println("绘制圆形,半径 $radius")
}

class Square(val side: Double) : Shape {
override fun draw() = println("绘制正方形,边长 $side")
}

// 伴生对象工厂
class ShapeFactory {
companion object {
fun createCircle(radius: Double) = Circle(radius)
fun createSquare(side: Double) = Square(side)

// 根据 type 创建
fun create(type: String, size: Double): Shape = when (type) {
"circle" -> Circle(size)
"square" -> Square(size)
else -> throw IllegalArgumentException("未知类型: $type")
}
}
}

fun main() {
val circle = ShapeFactory.createCircle(5.0)
val square = ShapeFactory.create("square", 4.0)
circle.draw()
square.draw()
}

建造者模式

Kotlin 使用默认参数和 DSL 风格实现建造者模式:

// 方式一:默认参数(推荐)
data class HttpRequest(
val url: String,
val method: String = "GET",
val headers: Map<String, String> = emptyMap(),
val body: String? = null,
val timeout: Int = 30000
)

fun main() {
val request = HttpRequest(
url = "https://api.example.com",
method = "POST",
headers = mapOf("Content-Type" to "application/json"),
body = """{"name": "test"}"""
)
}

// 方式二:DSL 风格构建器
class HttpRequestBuilder {
var url: String = ""
var method: String = "GET"
private val headers = mutableMapOf<String, String>()
var body: String? = null
var timeout: Int = 30000

fun header(name: String, value: String) {
headers[name] = value
}

fun build() = HttpRequest(url, method, headers.toMap(), body, timeout)
}

fun httpRequest(block: HttpRequestBuilder.() -> Unit): HttpRequest {
return HttpRequestBuilder().apply(block).build()
}

// 使用 DSL 创建
val dslRequest = httpRequest {
url = "https://api.example.com"
method = "POST"
header("Content-Type", "application/json")
body = """{"name": "test"}"""
}

策略模式

// 策略接口
interface DiscountStrategy {
fun calculate(price: Double): Double
}

// 具体策略
class NoDiscount : DiscountStrategy {
override fun calculate(price: Double) = price
}

class PercentageDiscount(private val percent: Double) : DiscountStrategy {
override fun calculate(price: Double) = price * (1 - percent / 100)
}

class FixedDiscount(private val amount: Double) : DiscountStrategy {
override fun calculate(price: Double) = maxOf(0.0, price - amount)
}

// 上下文
class ShoppingCart(private var strategy: DiscountStrategy = NoDiscount()) {
private val items = mutableListOf<Pair<String, Double>>()

fun addItem(name: String, price: Double) {
items.add(name to price)
}

fun setStrategy(newStrategy: DiscountStrategy) {
strategy = newStrategy
}

fun checkout(): Double {
val total = items.sumOf { it.second }
return strategy.calculate(total)
}
}

fun main() {
val cart = ShoppingCart()
cart.addItem("商品A", 100.0)
cart.addItem("商品B", 200.0)

println("原价: ${cart.checkout()}") // 300.0

cart.setStrategy(PercentageDiscount(10.0))
println("9折: ${cart.checkout()}") // 270.0

cart.setStrategy(FixedDiscount(50.0))
println("减50: ${cart.checkout()}") // 250.0
}

观察者模式

// 观察者接口
interface Observer<T> {
fun onUpdate(data: T)
}

// 被观察者
class Observable<T> {
private val observers = mutableListOf<Observer<T>>()

fun subscribe(observer: Observer<T>) {
observers.add(observer)
}

fun unsubscribe(observer: Observer<T>) {
observers.remove(observer)
}

fun notify(data: T) {
observers.forEach { it.onUpdate(data) }
}
}

// 实际应用:新闻发布
class NewsAgency : Observable<String>() {
fun publish(news: String) {
println("发布新闻: $news")
notify(news)
}
}

class NewsReader(val name: String) : Observer<String> {
override fun onUpdate(data: String) {
println("$name 收到新闻: $data")
}
}

fun main() {
val agency = NewsAgency()
val reader1 = NewsReader("张三")
val reader2 = NewsReader("李四")

agency.subscribe(reader1)
agency.subscribe(reader2)

agency.publish("Kotlin 2.0 发布!")
}

小结

本章我们全面学习了 Kotlin 面向对象编程:

  1. 类与对象:主/次构造函数、属性、初始化块
  2. 继承:open 类、覆盖方法、多态
  3. 接口:抽象方法、默认实现、多继承
  4. 数据类:自动生成 equals、hashCode、toString、copy
  5. 密封类:受限继承、穷尽检查
  6. 枚举类:命名常量、带属性的枚举
  7. 对象与伴生对象:单例模式、静态成员
  8. 嵌套类与内部类:静态嵌套、内部引用
  9. 设计模式:单例、工厂、建造者、策略、观察者

Kotlin 的面向对象特性既保留了 Java 的优点,又引入了许多现代化的改进,让代码更加简洁、安全、表达力强。

练习

  1. 创建一个 BankAccount 类,包含账户余额、存款、取款方法,并实现线程安全
  2. 使用密封类实现一个简单的状态机(如订单状态流转)
  3. 使用数据类和密封类实现一个网络请求结果封装
  4. 实现一个通用的 Repository 模式,支持不同的数据源
  5. 使用策略模式实现不同的排序算法

参考资料