Kotlin: Kotlin对象表达式与伴生对象详解
最后更新:2026-08-26
Kotlin 用 关键字统一了三种形态:单例声明、伴生对象、匿名对象——Charlie 用 替代 Java 的 工厂方法,用 替代单例工具类。
1. 你将学到
- 声明:Kotlin 单例模式
- :工厂方法与静态成员替代
- 对象表达式:匿名实现
- 伴生对象扩展:类级扩展函数
- Charlie 实战: 工厂 +
2. 一个架构师的真实故事
(1) 痛点:Java 单例与静态方法泛滥
Charlie 的 Java 项目有 10+ 个单例类()和大量静态工具方法(),测试困难且无法扩展。
(2) Kotlin object 的解法
KOTLIN
// Java: verbose singleton
public class OrderValidator {
private static final OrderValidator INSTANCE = new OrderValidator();
private OrderValidator() {}
public static OrderValidator getInstance() { return INSTANCE; }
}
// Kotlin: 1-line singleton
object OrderValidator {
fun validate(order: Order) = require(order.total >= 0)
}
一行代码替代 Java 的 5 行单例样板,且天然线程安全。
3. object 声明(单例)
(1) 基本语法
KOTLIN
// Singleton: only one instance, thread-safe initialization
object OrderValidator {
fun validate(order: Order): Boolean {
return order.total >= 0 && order.id.startsWith("ORD-")
}
}
// Usage: access directly by object name
OrderValidator.validate(order) // No getInstance() needed
(2) 带状态的单例
KOTLIN
object OrderCounter {
private var count = 0L
fun increment() { count++ }
fun getCount() = count
fun reset() { count = 0 }
}
OrderCounter.increment()
println(OrderCounter.getCount()) // 1
(3) object vs Java 单例
| 维度 | Java 单例 | Kotlin object |
|---|---|---|
| 代码量 | 5+ 行 | 1 行 |
| 线程安全 | 需手动保证 | 天然安全 |
| 序列化 | 需特殊处理 | 天然正确 |
| 可测试性 | 困难 | 同样困难(全局状态) |
| 替代方案 | 依赖注入 | 依赖注入(推荐) |
4. companion object(伴生对象)
(1) 基本语法
KOTLIN
class Order(val id: String, val total: Double, val status: String) {
companion object {
// Factory methods
fun create(id: String, total: Double): Order =
Order(id, total, "PENDING")
fun fromCsv(line: String): Order {
val parts = line.split(",")
return Order(parts[0], parts[1].toDouble(), parts[2])
}
// Constants
const val DEFAULT_STATUS = "PENDING"
const val MAX_TOTAL = 1_000_000.0
}
}
// Usage: like static in Java
val order = Order.create("ORD-001", 299.99)
val csvOrder = Order.fromCsv("ORD-002,1500.00,CONFIRMED")
println(Order.DEFAULT_STATUS)
(2) 命名伴生对象
KOTLIN
class Order(val id: String, val total: Double) {
companion object Factory {
fun create(id: String, total: Double) = Order(id, total)
}
}
// Can use either name
Order.create("ORD-001", 299.99)
Order.Factory.create("ORD-001", 299.99)
(3) companion object 实现接口
KOTLIN
interface JsonFactory`<T>` {
fun fromJson(json: String): T
}
class Order(val id: String, val total: Double) {
companion object : JsonFactory`<Order>` {
override fun fromJson(json: String): Order {
// Parse json and create Order
return Order("ORD-from-json", 0.0)
}
}
}
// Polymorphic usage
val factory: JsonFactory`<Order>` = Order
val order = factory.fromJson("{\"id\":\"ORD-001\"}")
(4) companion object vs Java static
| 维度 | Java static | Kotlin companion object |
|---|---|---|
| 性质 | 类级成员 | 对象(伴生单例) |
| 实现接口 | 不能 | 能 |
| 扩展函数 | 不能 | 能 |
| 命名 | 无名 | 可命名 |
| 运行时存在 | 不存在 | 存在(单例对象) |
5. 对象表达式(匿名对象)
(1) 基本语法
KOTLIN
// Anonymous object: like Java anonymous inner class
val handler = object : OrderHandler {
override fun handle(order: Order) {
println("Handling ${order.id}")
}
}
// Anonymous object implementing multiple interfaces
val multiHandler = object : OrderHandler, Loggable {
override fun handle(order: Order) { println("Handle: ${order.id}") }
override fun log(message: String) { println("[LOG] $message") }
}
(2) 匿名对象持有外部变量
KOTLIN
fun createHandler(prefix: String): OrderHandler {
var count = 0 // Captured by the anonymous object
return object : OrderHandler {
override fun handle(order: Order) {
count++
println("$prefix [#$count] ${order.id}")
}
}
}
(3) 三种 object 形态对比
| 形态 | 语法 | 生命周期 | 适用场景 |
|---|---|---|---|
| 声明 | 进程级单例 | 全局工具/配置 | |
| 类级单例 | 工厂方法/常量 | ||
| 对象表达式 | 临时实例 | 一次性实现 |
6. 实际应用场景对比
| 场景 | 推荐形态 | 示例 |
|---|---|---|
| 全局配置 | 声明 | |
| 工厂方法 | ||
| 一次性回调 | 对象表达式 | |
| 常量集合 | + | |
| 工具函数 | 声明或顶层函数 | |
| 接口多实现 | 对象表达式 |
7. 伴生对象扩展
KOTLIN
class Order(val id: String, val total: Double) {
companion object // Empty - exists as extension target
}
// Extension on companion object
fun Order.Companion.fromCsv(line: String): Order {
val parts = line.split(",")
return Order(parts[0], parts[1].toDouble())
}
// Usage
val order = Order.fromCsv("ORD-001,299.99")
💡 提示: 伴生对象扩展让第三方库能为类添加"静态方法",无需修改类源码。
7. object 形态选择流程
flowchart TD
A[需要 object?] --> B{与类强相关?}
B -->|Yes| C{需要工厂/常量?}
B -->|No| D{只需一次性实例?}
C -->|Yes| E[companion object]
C -->|No| F{需要全局唯一?}
D -->|Yes| G[对象表达式<br/>object : Interface]
D -->|No| H[普通类实例]
F -->|Yes| I[object 声明<br/>单例]
F -->|No| H
8. 完整示例
KOTLIN
// ============================================
// OrderProcessor - Object Patterns
// Feature: Singleton, Companion Factory, Anonymous Handler
// ============================================
data class Order(val id: String, val total: Double, var status: String, val customer: String) {
companion object {
const val DEFAULT_STATUS = "PENDING"
const val MAX_TOTAL = 1_000_000.0
fun create(id: String, total: Double, customer: String): Order {
require(total >= 0) { "Total must be non-negative" }
require(total <= MAX_TOTAL) { "Total exceeds maximum" }
return Order(id, total, DEFAULT_STATUS, customer)
}
fun bulkCreate(vararg ids: String, total: Double, customer: String): List`<Order>` {
return ids.map { create(it, total, customer) }
}
}
}
// Singleton validator
object OrderValidator {
private val rules = mutableListOf<(Order) -> Boolean>()
fun addRule(rule: (Order) -> Boolean) { rules.add(rule) }
fun validate(order: Order): List`<String>` {
val errors = mutableListOf`<String>`()
if (!order.id.startsWith("ORD-")) errors.add("Invalid ID format")
if (order.total < 0) errors.add("Negative total")
rules.forEach { rule ->
if (!rule(order)) errors.add("Custom rule failed")
}
return errors
}
}
// Singleton counter
object OrderMetrics {
private var totalProcessed = 0L
private var totalRevenue = 0.0
@Synchronized fun record(order: Order) {
totalProcessed++
totalRevenue += order.total
}
fun report() = "Processed: $totalProcessed orders, Revenue: \$$totalRevenue USD"
}
interface OrderHandler {
fun handle(order: Order): String
}
fun main() {
// Companion factory
println("=== Factory Creation ===")
val order1 = Order.create("ORD-001", 299.99, "Alice")
val order2 = Order.create("ORD-002", 15_000.00, "Bob")
val bulkOrders = Order.bulkCreate("ORD-010", "ORD-011", "ORD-012", total = 500.0, customer = "Charlie")
println(order1)
println(order2)
bulkOrders.forEach { println(it) }
// Singleton validator
println("\n=== Validation ===")
OrderValidator.addRule { it.total > 0 }
val validOrder = Order("ORD-100", 299.99, "PENDING", "Alice")
val invalidOrder = Order("BAD-001", -50.0, "PENDING", "Bob")
println("Valid: ${OrderValidator.validate(validOrder)}")
println("Invalid: ${OrderValidator.validate(invalidOrder)}")
// Singleton metrics
println("\n=== Metrics ===")
listOf(order1, order2).forEach { OrderMetrics.record(it) }
println(OrderMetrics.report())
// Anonymous handler
println("\n=== Anonymous Handler ===")
val shippingHandler = object : OrderHandler {
private var shipped = 0
override fun handle(order: Order): String {
shipped++
order.status = "SHIPPED"
return "Shipped ${order.id} (Total shipped: $shipped)"
}
}
println(shippingHandler.handle(order1))
println(shippingHandler.handle(order2))
}
输出:
TEXT
📖 仅展示
=== Factory Creation ===
Order(id=ORD-001, total=299.99, status=PENDING, customer=Alice)
Order(id=ORD-002, total=15000.0, status=PENDING, customer=Bob)
Order(id=ORD-010, total=500.0, status=PENDING, customer=Charlie)
Order(id=ORD-011, total=500.0, status=PENDING, customer=Charlie)
Order(id=ORD-012, total=500.0, status=PENDING, customer=Charlie)
=== Validation ===
Valid: []
Invalid: [Invalid ID format, Negative total, Custom rule failed]
=== Metrics ===
Processed: 2 orders, Revenue: $15299.99 USD
=== Anonymous Handler ===
Shipped ORD-001 (Total shipped: 1)
Shipped ORD-002 (Total shipped: 2)
❓ 常见问题
Q object 单例可以注入依赖吗?
A 技术上可以(通过 var 属性),但不推荐。单例全局状态使测试困难。生产代码推荐用依赖注入框架(Koin/Dagger)而非 object 单例。
Q companion object 的成员真的是 static 吗?
A 在 JVM 上,companion object 内用 注解的方法会编译为真正的 static 方法。不加注解则是伴生对象上的实例方法。
Q 匿名对象能访问外部变量吗?
A 能,与 Java 匿名内部类不同,Kotlin 匿名对象可以访问和修改外部的 var 变量(类似闭包)。
Q companion object 和 object 声明怎么选择?
A 与类强相关的用 companion object(工厂方法、常量),独立于任何类的用 object 声明(全局工具、配置)。
Q 和普通 在 companion object 中有什么区别?
A 是编译期常量(内联到调用处),只适用于基本类型和 String。普通 是运行时初始化。
Q companion object 可以有 supertype 吗?
A 可以。companion object 可以实现接口,这让工厂模式更优雅:
<Order>。📖 小节
- 声明创建线程安全单例,替代 Java 的 5+ 行单例样板
- 替代 Java static,更强大(可接口、可扩展、可命名)
- 对象表达式 替代 Java 匿名内部类
- 伴生对象扩展允许为类添加"静态方法"而不修改源码
- 单例测试困难,生产代码优先依赖注入而非 object 单例
- 编译期常量 vs 运行时初始化
📝 作业
- 基础题(难度⭐):创建一个 单例,提供 和 方法。提示:
- 进阶题(难度⭐⭐):为 类添加 工厂方法 ,解析 CSV 行创建 Order。提示:
- 挑战题(难度⭐⭐⭐):实现伴生对象接口 + 伴生对象扩展 。提示: + 顶层扩展函数