反射¶
反射允许程序在运行时检查类、函数、属性、构造函数和注解等结构。Kotlin 的函数和属性是一等概念,因此 Kotlin 反射不只是 Java 反射的包装,它还提供了 KClass、KFunction、KProperty 等 Kotlin 语义。
JVM 依赖¶
在 JVM 上,Kotlin 反射功能位于单独的 kotlin-reflect 依赖中。这样不使用反射的应用可以避免额外体积。
Gradle Kotlin DSL:
dependencies {
implementation(kotlin("reflect"))
}
Maven:
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
如果只用 SomeClass::class 做简单类引用,未必需要完整反射库;但要枚举成员、读取注解、调用函数等,通常需要 kotlin-reflect。
类引用:KClass¶
val klass = User::class
println(klass.simpleName)
对象实例的运行时类:
val user: Any = User("Ada")
println(user::class.qualifiedName)
JVM 上,Kotlin class reference 和 Java class reference 不完全一样:
val kClass = User::class
val javaClass = User::class.java
Java 的 Class<?> 转 Kotlin KClass:
val kClass = javaClass.kotlin
函数引用¶
fun isOdd(x: Int): Boolean = x % 2 != 0
val numbers = listOf(1, 2, 3, 4)
println(numbers.filter(::isOdd))
::isOdd 可以作为函数类型 (Int) -> Boolean 使用,也可以作为 KFunction 参与反射。
成员函数引用:
val lengthFunction = String::length
更常见的是方法引用:
val names = users.map(User::name)
这里 User::name 是属性引用,不是函数引用。
属性引用¶
data class User(val name: String, val age: Int)
val property = User::name
println(property.get(User("Ada", 36)))
顶层属性:
val appName = "Demo"
fun main() {
println(::appName.name)
println(::appName.get())
}
可变属性引用:
var counter = 0
fun main() {
::counter.set(10)
println(::counter.get())
}
构造函数引用¶
data class User(val name: String)
val factory = ::User
val user = factory("Ada")
这在依赖注入、工厂注册、测试数据构建中可能有用。
绑定引用¶
普通引用还需要接收者:
val unbound = String::uppercase
println(unbound("kotlin"))
绑定引用固定了接收者:
val text = "kotlin"
val bound = text::uppercase
println(bound())
读取注解¶
annotation class Route(val path: String)
@Route("/users")
class UserController
fun main() {
val route = UserController::class.annotations
.filterIsInstance<Route>()
.firstOrNull()
println(route?.path)
}
注意:只有运行时保留的注解才能通过反射读取:
@Retention(AnnotationRetention.RUNTIME)
annotation class RuntimeAnnotation
Kotlin 反射 vs Java 反射¶
Java 反射:
Class<?> clazz = User.class;
Method method = clazz.getDeclaredMethod("getName");
Kotlin 反射:
val clazz = User::class
val properties = clazz.members
Kotlin 反射更理解 Kotlin 概念,例如:
- 属性。
- 主构造函数。
- 可空性。
- 参数名。
- Kotlin 注解目标。
Java 反射更贴近 JVM 字节码,例如字段、方法、构造器。与 Java 框架互操作时,两者可能都需要理解。
反射的代价¶
反射很强,但不是免费:
- 增加运行时依赖体积。
- 运行时访问比直接调用慢。
- 更难被编译器检查。
- 混淆、裁剪、Native 镜像、Android R8/ProGuard 场景需要额外配置。
业务代码能用普通类型、接口、多态或代码生成解决时,不要优先用反射。
常见使用场景¶
适合:
- 序列化和反序列化框架。
- 依赖注入容器。
- ORM 和映射框架。
- 测试工具。
- 通用配置绑定。
- 插件系统。
不适合:
- 普通业务分支判断。
- 绕过可见性设计。
- 为了少写几行代码动态调用方法。
实践建议¶
- JVM 项目使用完整 Kotlin 反射前确认已加入
kotlin-reflect。 - 只需要 Java 框架识别时,可能 Java 反射和注解 target 更重要。
- 运行时读取注解时确认
AnnotationRetention.RUNTIME。 - Android 和 Native 场景谨慎使用反射,关注体积和裁剪配置。
- 公共 API 设计优先用类型系统表达,不要把反射当成默认扩展点。
参考¶
- 官方反射文档:https://kotlinlang.org/docs/reflection.html