Kotlin: Kotlin对象表达式与伴生对象详解

最后更新:2026-08-26

Kotlin 用 关键字统一了三种形态:单例声明、伴生对象、匿名对象——Charlie 用 替代 Java 的 工厂方法,用 替代单例工具类。

1. 你将学到


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 形态选择流程

100%
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>

📖 小节


📝 作业

  1. 基础题(难度⭐):创建一个 单例,提供 和 方法。提示:
  2. 进阶题(难度⭐⭐):为 类添加 工厂方法 ,解析 CSV 行创建 Order。提示:
  3. 挑战题(难度⭐⭐⭐):实现伴生对象接口 + 伴生对象扩展 。提示: + 顶层扩展函数

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏