2-1-5 快速掌握Kotlin-对象类型

40 阅读4分钟

Kotlin 对象类型

Kotlin 中的对象类型系统非常丰富,主要包括类和对象、数据类、密封类、枚举类等。以下是详细的分类和用法:

1. 类和对象

基本类定义

// 简单类
class Person {
    var name: String = ""
    var age: Int = 0
    
    fun introduce() {
        println("I'm $name, $age years old")
    }
}

// 带主构造器的类
class Person(val name: String, var age: Int) {
    // 属性已经在主构造器中声明
    
    fun greet() = "Hello, I'm $name"
}

// 带初始化块的类
class Person(name: String, age: Int) {
    val name: String
    var age: Int
    
    init {
        this.name = name
        this.age = age
        println("Person created: $name")
    }
}

构造器

// 主构造器 + 次构造器
class Person(val name: String, var age: Int) {
    
    constructor(name: String) : this(name, 0) {
        println("使用次构造器")
    }
    
    constructor() : this("Unknown", 0)
}

// 使用
val p1 = Person("Alice", 25)  // 主构造器
val p2 = Person("Bob")        // 次构造器
val p3 = Person()             // 无参次构造器

属性和字段

class Person {
    // 属性
    var name: String = ""
    
    // 计算属性(只读)
    val isAdult: Boolean
        get() = age >= 18
    
    // 属性带自定义访问器
    var age: Int = 0
        set(value) {
            if (value >= 0) {
                field = value
            } else {
                println("年龄不能为负数")
            }
        }
        get() {
            println("读取年龄: $field")
            return field
        }
    
    // 延迟初始化属性
    lateinit var nickname: String
    
    // 常量属性
    companion object {
        const val MAX_AGE = 150
    }
}

2. 数据类 (Data Class)

专门用于存储数据的类,自动生成常用方法。

// 基本数据类
data class Person(
    val name: String,
    var age: Int,
    val city: String = "Unknown"
)

// 使用
val person1 = Person("Alice", 25, "New York")
val person2 = Person("Alice", 25, "New York")
val person3 = person1.copy(age = 26)  // 复制并修改

// 自动生成的方法
println(person1 == person2)  // true - equals()
println(person1.hashCode())  // hashCode()
println(person1.toString())  // "Person(name=Alice, age=25, city=New York)"
println(person1.component1()) // name - 解构声明支持

// 解构声明
val (name, age, city) = person1
println("$name is $age years old from $city")

限制和注意事项

// 数据类必须满足的条件:
// 1. 主构造器至少有一个参数
// 2. 所有主构造器参数必须是 val 或 var
// 3. 不能是 abstract、open、sealed 或 inner

// 可以额外定义属性和方法
data class User(
    val id: Int,
    val name: String
) {
    var isActive: Boolean = true  // 不在 toString/equals/hashCode 中
    
    fun displayInfo() = "[$id] $name"
}

// 通过 componentN 方法支持解构
operator fun User.component3() = isActive

3. 密封类 (Sealed Class)

用于表示受限的类层次结构,类似枚举的扩展。

// 定义密封类
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

// 使用(when 表达式是完备的)
fun handleResult(result: Result<String>) {
    when (result) {
        is Result.Success -> println("成功: ${result.data}")
        is Result.Error -> println("错误: ${result.exception}")
        Result.Loading -> println("加载中...")
        // 不需要 else 分支,因为所有情况都已覆盖
    }
}

// 嵌套密封类
sealed class Expression {
    data class Number(val value: Int) : Expression()
    data class Sum(val left: Expression, val right: Expression) : Expression()
    data class Multiply(val left: Expression, val right: Expression) : Expression()
}

// 递归计算表达式
fun eval(expr: Expression): Int = when (expr) {
    is Expression.Number -> expr.value
    is Expression.Sum -> eval(expr.left) + eval(expr.right)
    is Expression.Multiply -> eval(expr.left) * eval(expr.right)
}

4. 枚举类 (Enum Class)

// 基本枚举
enum class Color {
    RED, GREEN, BLUE
}

// 带属性的枚举
enum class Direction(val degrees: Int) {
    NORTH(0),
    EAST(90),
    SOUTH(180),
    WEST(270);
    
    fun opposite(): Direction = when (this) {
        NORTH -> SOUTH
        EAST -> WEST
        SOUTH -> NORTH
        WEST -> EAST
    }
}

// 枚举实现接口
enum class LogLevel : Comparable<LogLevel> {
    DEBUG, INFO, WARN, ERROR;
    
    override fun compareTo(other: LogLevel): Int {
        return this.ordinal - other.ordinal
    }
}

// 使用
val color = Color.RED
println(color.name)      // "RED"
println(color.ordinal)   // 0

// 遍历枚举
for (direction in Direction.values()) {
    println("${direction.name}: ${direction.degrees}°")
}

// 根据名称获取枚举
val blue = Color.valueOf("BLUE")
val maybeColor = enumValueOfOrNull<Color>("PURPLE")  // Kotlin 1.9+

5. 对象表达式和对象声明

对象表达式(匿名对象)

// 创建匿名对象
val clickListener = object : ClickListener {
    override fun onClick() {
        println("Clicked!")
    }
    
    fun extraMethod() {
        println("额外方法")
    }
}

// 继承多个类型
val multiInterface = object : ClickListener, HoverListener {
    override fun onClick() { /* ... */ }
    override fun onHover() { /* ... */ }
}

// 不继承任何类的对象
val adHocObject = object {
    val x = 10
    val y = 20
    fun sum() = x + y
}
println(adHocObject.sum())  // 30

对象声明(单例)

// 对象声明(单例)
object Singleton {
    private var counter = 0
    
    fun increment() {
        counter++
    }
    
    fun getCount() = counter
    
    // 初始化代码
    init {
        println("Singleton initialized")
    }
}

// 使用
Singleton.increment()
println(Singleton.getCount())  // 1

// 伴生对象
class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
        
        const val VERSION = "1.0"
    }
}

// 使用伴生对象
val instance = MyClass.create()
val version = MyClass.VERSION

// 匿名伴生对象
class AnotherClass {
    companion object {
        @JvmStatic  // 在Java中作为静态方法访问
        fun staticMethod() {
            println("静态方法")
        }
    }
}

6. 嵌套类和内部类

class Outer {
    private val outerProperty = "Outer"
    
    // 嵌套类(静态内部类)
    class Nested {
        fun foo() {
            // 不能访问 outerProperty
            println("Nested class")
        }
    }
    
    // 内部类(持有外部类引用)
    inner class Inner {
        fun bar() {
            println("Inner class accessing: $outerProperty")
        }
    }
    
    // 局部类
    fun createLocalClass(): Comparable<String> {
        class LocalClass(val value: String) : Comparable<String> {
            override fun compareTo(other: String): Int {
                return value.compareTo(other)
            }
        }
        return LocalClass("test")
    }
}

// 使用
val nested = Outer.Nested()  // 不需要 Outer 实例
val outer = Outer()
val inner = outer.Inner()    // 需要 Outer 实例

7. 抽象类和接口

// 接口
interface Drawable {
    fun draw()  // 抽象方法
    
    // 默认实现
    fun prepare() {
        println("准备绘制")
    }
    
    // 属性
    val color: String
}

// 抽象类
abstract class Shape(val name: String) : Drawable {
    abstract val area: Double
    
    // 具体方法
    fun displayInfo() {
        println("形状: $name, 面积: $area")
    }
}

// 实现
class Circle(val radius: Double) : Shape("圆形") {
    override val area: Double
        get() = Math.PI * radius * radius
    
    override val color: String = "Red"
    
    override fun draw() {
        println("绘制圆形,半径: $radius")
    }
    
    // 重写默认实现
    override fun prepare() {
        super.prepare()
        println("特别准备圆形绘制")
    }
}

8. 泛型类和类型参数

// 泛型类
class Box<T>(var content: T) {
    fun getContent(): T = content
    fun setContent(newContent: T) {
        content = newContent
    }
    
    // 泛型方法
    fun <R> transform(transformer: (T) -> R): Box<R> {
        return Box(transformer(content))
    }
}

// 使用
val intBox = Box(42)
val stringBox = Box("Hello")

// 类型投影(协变/逆变)
interface Source<out T> {  // 协变 - 只能作为返回值
    fun next(): T
}

interface Consumer<in T> {  // 逆变 - 只能作为参数
    fun consume(item: T)
}

// 星投影
fun printItems(items: List<*>) {
    for (item in items) {
        println(item)
    }
}

9. 内联类 (Inline Class)

值类型的包装类,运行时会被优化掉。

// 内联类(Kotlin 1.3+,1.5+ 稳定)
@JvmInline
value class Password(val value: String) {
    // 可以定义方法和属性
    val length: Int
        get() = value.length
    
    fun isValid(): Boolean = value.length >= 8
    
    init {
        require(value.length >= 8) { "密码必须至少8位" }
    }
}

// 使用 - 运行时只有 String,没有 Password 对象
val password = Password("secret123")
println(password.length)  // 8

// 支持解构
val (rawPassword) = password

// 内联类作为类型别名,但有更强的类型安全
typealias EmailAlias = String
@JvmInline value class Email(val value: String)

fun sendEmail(email: Email) { /* ... */ }
// sendEmail("test@example.com")  // 编译错误 - 类型不匹配
sendEmail(Email("test@example.com"))  // 正确

10. 类型检查和转换

// 类型检查
val obj: Any = "Hello"

if (obj is String) {
    println(obj.length)  // 智能转换
}

// 安全转换
val str: String? = obj as? String  // 失败返回 null

// 类型擦除和具体化类型参数(内联函数)
inline fun <reified T> isInstance(obj: Any): Boolean {
    return obj is T
}

// 使用
println(isInstance<String>("test"))  // true
println(isInstance<Int>("test"))     // false

最佳实践

  1. 优先使用数据类存储数据
  2. 使用密封类表达受限的类层次
  3. 考虑内联类包装基本类型以增加类型安全
  4. 伴生对象替代 Java 的静态方法
  5. 对象声明实现单例模式
  6. 合理使用嵌套/内部类,避免不必要的内存泄漏

Kotlin 的对象类型系统提供了强大而灵活的工具,可以帮助你编写更安全、更表达力的代码。