Kotlin 反射 API
1. 反射的基本概念
反射(Reflection)是程序在运行时动态获取类信息、操作类属性或方法的能力。Kotlin 反射 API 基于 Java 反射,但提供了更简洁的语法和额外的功能。
1.1 反射的典型用途
- 动态加载类
- 运行时检查类结构(属性、方法等)
- 动态调用方法或访问属性
- 实现通用框架(如序列化、依赖注入)
2. Kotlin 反射 API 核心类
Kotlin 反射主要通过 kotlin-reflect 库实现,核心类位于 kotlin.reflect 包中:
| 类/接口 | 说明 |
|---|---|
KClass | 表示类的引用,类似于 Java 的 Class |
KProperty | 表示属性,包括扩展属性 |
KFunction | 表示函数,包括扩展函数 |
KType | 表示类型,包含泛型信息 |
KCallable | 可调用元素的超接口(函数和属性都继承此接口) |
3. 基本反射操作
3.1 获取类的引用
// 1. 通过类名获取
val cls = MyClass::class
// 2. 通过对象实例获取
val obj = MyClass()
val cls2 = obj::class
// 3. 获取 Java Class 后转换
val javaClass = MyClass::class.java
3.2 获取类信息
data class Person(val name: String, val age: Int)
fun inspectClass(cls: KClass<*>) {
println("类名: ${cls.simpleName}")
println("属性:")
cls.memberProperties.forEach { prop ->
println(" ${prop.name}: ${prop.returnType}")
}
println("函数:")
cls.memberFunctions.forEach { func ->
println(" ${func.name}()")
}
}
// 使用示例
inspectClass(Person::class)
3.3 动态创建实例
class User(val name: String)
fun createInstance(cls: KClass<*>, vararg args: Any?): Any? {
val constructor = cls.constructors.firstOrNull()
return constructor?.call(*args)
}
// 使用示例
val user = createInstance(User::class, "Alice") as User
println(user.name) // 输出: Alice
3.4 动态访问属性和方法
class Calculator {
val version = "1.0"
fun add(a: Int, b: Int) = a + b
}
fun main() {
val calc = Calculator()
val kClass = calc::class
// 访问属性
val versionProp = kClass.memberProperties.find { it.name == "version" }
println(versionProp?.get(calc)) // 输出: 1.0
// 调用方法
val addFunc = kClass.memberFunctions.find { it.name == "add" }
val result = addFunc?.call(calc, 2, 3)
println(result) // 输出: 5
}
4. 高级反射特性
4.1 获取泛型类型信息
inline fun <reified T> getTypeInfo() {
val type = typeOf<T>()
println("Type: ${type.classifier}")
println("Arguments: ${type.arguments.joinToString()}")
}
// 使用示例
getTypeInfo<List<String>>()
// 输出:
// Type: kotlin.collections.List
// Arguments: kotlin.String
4.2 可调用引用
Kotlin 支持将函数和属性转换为可调用引用:
fun square(x: Int) = x * x
fun main() {
// 函数引用
val squareRef = ::square
println(squareRef(5)) // 输出: 25
// 属性引用
val str = "Hello"
val lengthRef = String::length
println(lengthRef(str)) // 输出: 5
}
4.3 反射与注解
结合反射可以检查和处理注解:
@Target(AnnotationTarget.CLASS)
annotation class Table(val name: String)
@Table(name = "users")
class User
fun main() {
val tableAnnotation = User::class.annotations.find { it is Table } as? Table
println(tableAnnotation?.name) // 输出: users
}
5. 性能考虑与最佳实践
- 性能影响:反射操作比直接代码调用慢,应避免在性能关键路径使用
- 缓存反射结果:重复使用的反射对象应该缓存
- 使用
KCallable接口:统一处理属性和方法 - 结合
reified类型参数:在内联函数中获取类型信息
inline fun <reified T> createWithReflection(): T {
return T::class.constructors.first().call()
}
6. 实际应用示例:简易 ORM 框架
// 定义实体注解
@Target(AnnotationTarget.CLASS)
annotation class Entity(val tableName: String)
@Target(AnnotationTarget.PROPERTY)
annotation class Column(val name: String)
// 实体类
@Entity(tableName = "users")
data class User(
@Column(name = "user_id")
val id: Int,
@Column(name = "user_name")
val name: String
)
// 反射工具类
object ORMUtils {
fun <T : Any> createTable(entity: KClass<T>): String {
val tableName = entity.annotations
.filterIsInstance<Entity>()
.firstOrNull()?.tableName ?: entity.simpleName?.lowercase() ?: "table"
val columns = entity.memberProperties.map { prop ->
val columnName = prop.annotations
.filterIsInstance<Column>()
.firstOrNull()?.name ?: prop.name
"$columnName ${mapType(prop.returnType)}"
}
return "CREATE TABLE $tableName (${columns.joinToString()})"
}
private fun mapType(type: KType): String = when(type.classifier) {
Int::class -> "INTEGER"
String::class -> "TEXT"
else -> "TEXT"
}
}
// 使用示例
fun main() {
val createSQL = ORMUtils.createTable(User::class)
println(createSQL)
// 输出: CREATE TABLE users (user_id INTEGER, user_name TEXT)
}
总结
Kotlin 反射 API 提供了强大的运行时自省能力,虽然性能开销较大,但在框架开发、动态功能实现等场景非常有用。合理使用反射可以极大增强程序的灵活性和扩展性。
注意:使用反射需要添加依赖:
dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0") }
