Kotlin: Kotlin类与对象详解

最后更新:2026-08-26

Kotlin 的类设计哲学:主构造器 + 属性一步到位,init 块替代 Java 的构造器体——Charlie 用 3 行代码定义的 Order 类比 Java 的 30 行还强。

1. 你将学到


2. 一个架构师的真实故事

(1) 痛点:Java 构造器地狱

Charlie 在 Java 中定义 Order 类需要 3 个构造器(无参、必填、全参),加上 getter/setter/验证逻辑,一个领域类就 80+ 行代码。

(2) Kotlin 主构造器的解法

KOTLIN
// Java: 80+ lines for one domain class
// Kotlin: 3 lines with validation
class Order(val id: String, var status: String, val total: BigDecimal) {
    init { require(total >= BigDecimal.ZERO) { "Total must be non-negative" } }
}

主构造器 + 属性 + init 块三合一,80 行 Java → 3 行 Kotlin。


3. 主构造器与属性

(1) 主构造器一步到位

KOTLIN
// Primary constructor with properties in one line
class Order(val id: String, var status: String, val total: Double)

// Equivalent Java would be 20+ lines

(2) 构造器参数 vs 属性

KOTLIN
// val/var in constructor = property
class Order(val id: String,    // read-only property
            var status: String, // mutable property
            total: Double)      // just constructor param, not a property

// Usage
val order = Order("ORD-001", "PENDING", 299.99)
println(order.id)      // OK - val property
order.status = "PAID"  // OK - var property
// order.total         // ERROR - not a property

(3) 主构造器 vs 次构造器

KOTLIN
class Order(val id: String, var status: String, val total: Double) {
    // Secondary constructor MUST delegate to primary
    constructor(id: String) : this(id, "PENDING", 0.0)

    // Another secondary constructor
    constructor(id: String, total: Double) : this(id, "PENDING", total)
}

val order1 = Order("ORD-001")               // Secondary
val order2 = Order("ORD-002", 299.99)        // Secondary
val order3 = Order("ORD-003", "CONFIRMED", 1_500.00)  // Primary

(4) 构造器方式对比

维度 Java Kotlin
定义属性+构造 分开写 主构造器一行搞定
多构造器 独立定义 次构造器必须委托主构造
参数变属性 手动赋值 / 自动
默认值 方法重载 默认参数

4. init 块

块在主构造器执行后立即运行,用于验证和初始化逻辑。

(1) 基本用法

KOTLIN
class Order(val id: String, var status: String, val total: Double) {
    init {
        require(total >= 0.0) { "Total must be non-negative, got $total" }
        require(id.startsWith("ORD-")) { "Order ID must start with ORD-" }
    }

    // Multiple init blocks execute in order
    init {
        println("Order $id created with total \$$total USD")
    }
}

(2) init 块执行顺序

KOTLIN
class Example {
    val a = println("1: property initialization")

    init {
        println("2: first init block")
    }

    val b = println("3: property initialization")

    init {
        println("4: second init block")
    }
}
// Output: 1, 2, 3, 4 (declaration order)

5. 可见性修饰符

(1) 四种可见性

KOTLIN
class OrderProcessor {
    // public (default): visible everywhere
    fun process(order: Order) { ... }

    // private: visible inside this class only
    private fun validate(order: Order) { ... }

    // protected: visible in this class and subclasses
    protected fun calculateTax(order: Order) { ... }

    // internal: visible within the same module
    internal fun report() { ... }
}

(2) 可见性对比表

修饰符 类内 子类 同模块 全局

(3) Java vs Kotlin 可见性差异

维度 Java Kotlin
默认可见性 package-private
模块可见
包可见 package-private 无(用 internal 替代)
顶层声明 只有 public public / internal / private

6. 延迟初始化

(1) lateinit var

KOTLIN
class OrderService {
    // lateinit: promise to initialize before use
    lateinit var repository: OrderRepository

    fun init(repo: OrderRepository) {
        repository = repo
    }

    fun process(order: Order) {
        // Access before init throws UninitializedPropertyAccessException
        repository.save(order)
    }
}

(2) by lazy

KOTLIN
class OrderProcessor {
    // lazy: thread-safe, initialized on first access
    val cache: OrderCache by lazy {
        println("Initializing cache...")
        OrderCache(maxSize = 10_000)
    }

    // lazy with custom lock mode
    val heavyResource by lazy(LazyThreadSafetyMode.PUBLICATION) {
        loadHeavyResource()
    }
}

(3) lateinit vs by lazy 对比

维度
类型 (可变) (只读)
初始化时机 手动赋值 首次访问时
线程安全 是(默认)
可空性 非空声明 非空声明
未初始化访问 运行时异常 不会发生
适用场景 DI 框架注入 计算成本高的属性

7. 类关系图

100%
classDiagram
    class Order {
        +val id: String
        +var status: String
        +val total: Double
        +val items: List~OrderItem~
        +fun addItem(item: OrderItem)
    }
    class OrderItem {
        +val sku: String
        +val quantity: Int
        +val unitPrice: Double
        +fun subtotal: Double
    }
    class Customer {
        +val id: String
        +val name: String
        +val email: String?
        +val address: Address?
    }
    class Address {
        +val street: String
        +val city: String
        +val country: String
    }
    Order --> OrderItem : contains
    Order --> Customer : belongs to
    Customer --> Address : has

8. 完整示例:OrderProcessor 领域模型

KOTLIN
// ============================================
// OrderProcessor - Domain Model
// Feature: Order, OrderItem, Customer with init validation
// ============================================

import java.math.BigDecimal
import java.math.RoundingMode

class Address(val street: String, val city: String, val country: String) {
    override fun toString(): String = "$street, $city, $country"
}

class Customer(val id: String, val name: String, val email: String?) {
    var address: Address? = null

    fun getDisplayEmail(): String = email ?: "no-email"

    override fun toString(): String = "Customer($id, $name, ${getDisplayEmail()})"
}

class OrderItem(val sku: String, val quantity: Int, val unitPrice: BigDecimal) {
    init {
        require(quantity > 0) { "Quantity must be positive, got $quantity" }
        require(unitPrice >= BigDecimal.ZERO) { "Price must be non-negative" }
    }

    val subtotal: BigDecimal
        get() = unitPrice.multiply(BigDecimal(quantity)).setScale(2, RoundingMode.HALF_UP)
}

class Order(
    val id: String,
    var status: String,
    private val _items: MutableList`<OrderItem>` = mutableListOf()
) {
    init {
        require(id.startsWith("ORD-")) { "Order ID must start with ORD-" }
    }

    val items: List`<OrderItem>` get() = _items.toList()

    val total: BigDecimal
        get() = _items.fold(BigDecimal.ZERO) { acc, item -> acc.add(item.subtotal) }

    fun addItem(item: OrderItem) {
        _items.add(item)
    }

    val itemCount: Int get() = _items.size

    // Lazy computed tax
    val tax by lazy {
        total.multiply(BigDecimal("0.08")).setScale(2, RoundingMode.HALF_UP)
    }

    override fun toString(): String = "Order($id, $status, ${itemCount} items, \$$total USD)"
}

fun main() {
    val customer = Customer("CUST-001", "Alice", "alice@example.com").also {
        it.address = Address("123 Main St", "New York", "US")
    }
    println(customer)
    println("Address: ${customer.address}")

    val order = Order("ORD-001", "PENDING")
    order.addItem(OrderItem("SKU-WIDGET", 3, BigDecimal("9.99")))
    order.addItem(OrderItem("SKU-GADGET", 1, BigDecimal("149.99")))
    order.addItem(OrderItem("SKU-DOOHICKEY", 5, BigDecimal("4.50")))

    println(order)
    println("Subtotal: \$$${order.total} USD")
    println("Tax: \$$${order.tax} USD")
    println("Grand Total: \$$${order.total.add(order.tax)} USD")

    order.status = "CONFIRMED"
    println("Status updated: ${order.status}")
}

输出:

TEXT 📖 仅展示
Customer(CUST-001, Alice, alice@example.com)
Address: 123 Main St, New York, US
Order(ORD-001, PENDING, 3 items, $199.42 USD)
Subtotal: $$199.42 USD
Tax: $$15.95 USD
Grand Total: $$215.37 USD
Status updated: CONFIRMED

❓ 常见问题

Q 主构造器和次构造器可以共存吗?
A 可以,但次构造器必须用 委托到主构造器。推荐用默认参数替代次构造器。
Q init 块可以有多个吗?
A 可以,多个 init 块按声明顺序执行。推荐合并为一个以避免混淆。
Q lateinit 能用于基本类型吗?
A 不能。lateinit 只能用于非基本类型(对象类型),因为基本类型有默认值。Int/Double 等用 或可空类型 + 默认值。
Q by lazy 的初始化是线程安全的吗?
A 默认是线程安全的(SYNCHRONIZED 模式)。如果确定单线程访问,可用 提升性能。
Q internal 可见性在 Maven/Gradle 多模块项目中如何生效?
A internal 限制在同一 Gradle 模块或 Maven 模块内可见。不同模块即使同一项目也不能访问 internal 成员。
Q 为什么 Kotlin 没有 Java 的 package-private?
A Kotlin 用 (模块级可见性)替代 package-private。包级可见性在 Java 中常被误用,模块级更符合现代项目结构。

📖 小节


📝 作业

  1. 基础题(难度⭐):定义一个 类(id: String, name: String, price: Double),用 init 块验证 price >= 0。提示:
  2. 进阶题(难度⭐⭐):设计一个 类,用 注入 ,用 初始化缓存。提示: +
  3. 挑战题(难度⭐⭐⭐):实现一个完整的 Order 领域模型,包含 Order/OrderItem/Customer,所有验证逻辑在 init 块中,items 使用防御性拷贝。提示:参考第 8 节完整示例

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

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

100%

🙏 帮我们做得更好

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

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