文章目录

Kotlin 反射:KClass 与 KProperty

发布于 2026-04-03 09:32:16 · 浏览 8 次 · 评论 0 条

Kotlin 反射:KClass 与 KProperty

Kotlin 提供了一套强大的反射 API,允许你在运行时检查类、函数、属性等程序结构。其中 KClassKProperty 是两个核心接口,分别用于描述类和属性的元信息。掌握它们能让你在不硬编码的情况下动态访问对象结构。


获取 KClass 对象

获取一个类型的 KClass 对象是使用 Kotlin 反射的第一步。Kotlin 通过“类引用”语法实现这一点。

  1. 使用双冒号操作符 ::class 获取具体类型的 KClass。例如:

    val stringClass: KClass<String> = String::class
  2. 调用 javaClass.kotlin 转换 Java 的 Class 对象为 KClass(适用于从 Java 互操作场景传入的对象):

    val obj = "hello"
    val kClass = obj.javaClass.kotlin
  3. 注意::class 返回的是编译期已知类型的 KClass;若要处理泛型或未知类型,需配合 reified 类型参数(仅限内联函数)。


使用 KClass 检查和创建实例

KClass 提供了多种方法来查询类的信息或创建新实例。

  1. 检查一个对象是否属于某个类:

    if (String::class.isInstance("text")) {
        // 成立
    }
  2. 获取类的简单名称或完整限定名:

    println(String::class.simpleName)    // 输出 "String"
    println(String::class.qualifiedName) // 输出 "kotlin.String"
  3. 创建类的新实例(要求类有无参构造函数且未被优化掉):

    class Person(val name: String = "Unknown")
    
    val personClass = Person::class
    val instance = personClass.objectInstance ?: personClass.constructors.first().call()

    注意:如果类是 object 单例,则 objectInstance 非空;否则需通过构造器手动调用。

  4. 遍历所有声明的成员属性:

    data class User(val id: Int, val name: String)
    
    User::class.memberProperties.forEach { prop ->
        println(prop.name)
    }
    // 输出: id, name

理解 KProperty

KProperty 是 Kotlin 中表示属性的反射接口。它分为 KProperty0(无接收者)、KProperty1(单接收者)、KProperty2(双接收者),最常见的是 KProperty1<T, R>,表示类型为 T 的对象拥有返回类型为 R 的属性。

  1. 获取属性引用:

    class Book(val title: String)
    
    val titleProp: KProperty1<Book, String> = Book::title
  2. 读取属性值:

    val book = Book("Kotlin in Action")
    val value = titleProp.get(book) // 等价于 book.title
  3. 检查属性是否可变(即是否为 var):

    class Config(var debug: Boolean = false)
    
    val prop = Config::debug
    if (prop is KMutableProperty1<*, *>) {
        prop.set(Config(), true) // 可写入
    }
  4. 获取属性名称和返回类型:

    println(titleProp.name) // "title"
    println(titleProp.returnType) // kotlin.String

动态访问对象属性(通用方案)

你可以编写一个通用函数,通过属性名字符串动态读取任意对象的属性值。

  1. 定义一个扩展函数,接受对象和属性名:

    fun Any.getPropertyValue(propertyName: String): Any? {
        return this::class.memberProperties
            .firstOrNull { it.name == propertyName }
            ?.get(this)
    }
  2. 调用该函数读取值:

    data class Product(val id: Int, val price: Double)
    
    val p = Product(101, 29.99)
    val idValue = p.getPropertyValue("id")    // 101
    val priceValue = p.getPropertyValue("price") // 29.99
  3. 注意:此方法依赖 memberProperties,默认只包含 public 属性。若需访问 private 或继承属性,需启用 kotlin-reflect 并配置 JVM 参数(生产环境慎用)。


KClass 与 KProperty 的典型应用场景

场景 说明 使用方式
序列化/反序列化库 如 kotlinx.serialization 在运行时读取属性 通过 KClass.memberProperties 遍历字段
ORM 框架映射 将数据库列映射到 Kotlin 属性 利用 KProperty.name 匹配列名
依赖注入容器 自动注入带注解的属性 检查 KProperty.annotations
调试工具 动态打印对象所有字段 遍历 memberPropertiesget()

启用反射支持

Kotlin 反射功能依赖 kotlin-reflect 库,它不会自动包含在标准库中。

  1. 添加依赖(以 Gradle 为例):

    implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0")
  2. 确认运行时类路径包含该 JAR,否则会抛出 NoClassDefFoundError

  3. 注意:反射会带来性能开销和包体积增长,避免在高频路径(如循环内部)使用。


安全与限制

  1. 不要假设所有属性都能被反射访问。privateinternal 或被 ProGuard/R8 混淆的属性可能无法获取。

  2. 避免在 Android 发布版中过度使用反射,因其会增加方法数并影响启动速度。

  3. 检查 KProperty 是否为 KMutableProperty 再尝试写入,否则会抛出 IllegalStateException

  4. 泛型类型擦除KProperty.returnType 在运行时无法保留泛型实参(如 List<String> 会显示为 List<*>),需结合其他机制处理。


示例:通用相等性比较

利用 KClassKProperty 实现一个忽略顺序的深度属性比较:

fun <T : Any> T.deepEquals(other: T): Boolean {
    if (this === other) return true
    if (this::class != other::class) return false

    return this::class.memberProperties.all { prop ->
        val value1 = prop.get(this)
        val value2 = prop.get(other)
        when {
            value1 == null && value2 == null -> true
            value1 == null || value2 == null -> false
            value1 is Any && value2 is Any -> 
                if (value1::class.isData) value1.deepEquals(value2) else value1 == value2
            else -> value1 == value2
        }
    }
}

使用

data class Point(val x: Int, val y: Int)
val p1 = Point(1, 2)
val p2 = Point(1, 2)
println(p1.deepEquals(p2)) // true

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文