1. 概述
反射(Reflection)是指在运行时动态地检查、加载和调用类、字段、方法的能力,即使在编译期并不知道这些元素的具体信息。
这在实际开发中用途广泛,尤其是一些框架如 Spring 就重度依赖反射机制来实现依赖注入、AOP 等功能。JVM 原生支持反射,因此所有基于 JVM 的语言都可以使用。不过,像 Kotlin 这样的现代语言在此基础上提供了更高级的 API,让反射操作更简洁、类型更安全。
✅ 使用场景包括:
- 动态创建对象
- 序列化/反序列化框架
- 注解处理器
- 测试框架中的自动扫描与调用
⚠️ 踩坑提示:反射性能开销较大,且破坏封装性,应避免在高频路径滥用。
2. Java 反射在 Kotlin 中的使用
Kotlin 完全兼容标准 Java 反射 API,可以直接使用 java.lang.Class
和 java.lang.reflect
包下的所有类。这意味着你可以无缝调用 Java 中熟悉的 .getClass()
、.getMethods()
等方法。
例如,获取某个 Kotlin 类的所有 public 方法:
MyClass::class.java.methods
这段代码的含义如下:
MyClass::class
:获取该类的 Kotlin 类型引用(KClass<MyClass>
).java
:将其转换为对应的java.lang.Class<MyClass>
.methods
:调用 Java 反射 API 的getMethods()
方法
这个方式对 Kotlin 类和 Java 类都有效,也适用于 Kotlin 特有的语法结构,比如 data class:
data class ExampleDataClass(
val name: String, var enabled: Boolean)
ExampleDataClass::class.java.methods.forEach(::println)
输出结果会包含自动生成的 getName()
、isEnabled()
、setEnabled()
以及 equals()
、hashCode()
、toString()
等方法。
值得注意的是,返回值会被 Kotlin 自动包装成 kotlin.Array<Method>
,因此你可以直接使用 forEach
、map
等函数式操作。
❌ 注意事项:虽然 Java 反射可用,但它无法感知 Kotlin 的语言特性(如默认参数、可空类型、内联函数等),所以在复杂场景下建议使用 Kotlin 自带的反射 API。
3. Kotlin 反射增强功能
Kotlin 提供了独立的反射库 kotlin-reflect
,它弥补了 Java 反射在处理 Kotlin 特有语法时的不足。这套 API 更贴近 Kotlin 语义,支持 data class、属性委托、默认参数、协程挂起函数等特性的元数据读取。
要使用 Kotlin 反射,需引入依赖:
implementation("org.jetbrains.kotlin:kotlin-reflect")
核心入口是 引用(Reference)机制,通过 ::
操作符获取类、方法、属性或注解的元数据描述。
3.1 Kotlin 类引用(KClass)
Kotlin 中所有类都可以通过 ::class
获取一个 KClass<T>
实例,这是 Kotlin 反射的核心入口。
获取方式有三种:
val listClass: KClass<List> = List::class // 类名直接引用
val name = "Baeldung"
val stringClass: KClass<String> = name::class // 实例上获取
val someClass: Class<MyType>
val kotlinClass: KClass<MyType> = someClass.kotlin // 从 Java Class 转换
拿到 KClass
后,可以查询丰富的元信息:
val stringClass = String::class
assertEquals("kotlin.String", stringClass.qualifiedName)
assertFalse(stringClass.isData)
assertFalse(stringClass.isCompanion)
assertFalse(stringClass.isAbstract)
assertTrue(stringClass.isFinal)
assertFalse(stringClass.isSealed)
✅ 支持判断的特性包括:
- 是否为 data class(
isData
) - 是否为 companion object(
isCompanion
) - 是否抽象(
isAbstract
)、final(isFinal
)、密封类(isSealed
)
还能遍历类层级关系,甚至访问伴生对象:
println(TestWithCompanion::class.companionObject) // KClass? of companion
println(TestWithCompanion::class.companionObjectInstance) // actual instance
println(TestObject::class.objectInstance) // singleton object instance
创建实例
可以通过 createInstance()
快速构造无参实例:
val listClass = ArrayList::class
val list = listClass.createInstance()
assertTrue(list is ArrayList)
⚠️ 注意:仅当类存在无参构造函数时才能成功调用,否则抛出异常。
也可以显式获取构造器并传参(见下一节方法引用)。
成员访问
KClass
提供多种成员访问接口:
val bigDecimalClass = BigDecimal::class
println(bigDecimalClass.constructors) // 所有构造函数
println(bigDecimalClass.functions) // 所有方法(含扩展函数)
println(bigDecimalClass.memberProperties) // 成员属性
println(bigDecimalClass.memberExtensionFunctions) // 成员上的扩展函数
这些集合返回的是 Kotlin 原生的反射类型(如 KFunction
、KProperty
),比 Java 的 Method
更丰富。
3.2 Kotlin 方法引用(KFunction)
除了类,我们还可以通过 ::
获取方法的引用,用于调用或元信息分析。
基本用法示例:
val str = "Hello"
val lengthMethod = str::length
assertEquals(5, lengthMethod()) // 直接调用,称为 callable reference
这种方式不仅能调用,还能获取方法的详细信息:
val byteInputStream = String::byteInputStream
assertEquals("byteInputStream", byteInputStream.name)
assertFalse(byteInputStream.isSuspend)
assertFalse(byteInputStream.isExternal)
assertTrue(byteInputStream.isInline)
assertFalse(byteInputStream.isOperator)
关键属性说明:
属性 | 含义 |
---|---|
isSuspend |
是否为 suspend 函数(协程) |
isInline |
是否为 inline 函数 |
isOperator |
是否为操作符重载函数 |
isExternal |
是否为 external 函数 |
参数与返回值信息
通过方法引用还可深入查看参数和返回类型,包括可空性和默认值等 Kotlin 特性:
val str = "Hello"
val method = str::byteInputStream
assertEquals(ByteArrayInputStream::class.starProjectedType, method.returnType)
assertFalse(method.returnType.isMarkedNullable)
assertEquals(1, method.parameters.size)
assertTrue(method.parameters[0].isOptional) // 参数有默认值
assertFalse(method.parameters[0].isVararg)
assertEquals(Charset::class.starProjectedType, method.parameters[0].type)
✅ starProjectedType
是星投影类型,相当于 List<*>
,常用于泛型擦除后的表示。
3.3 Kotlin 属性引用(KProperty)
属性引用的工作方式与方法类似,但关注的是 val
/ var
定义的字段。
示例:
lateinit var mutableProperty: String
val mProperty = this::mutableProperty
assertEquals("mutableProperty", mProperty.name)
assertTrue(mProperty.isLateinit)
assertFalse(mProperty.isConst)
assertTrue(mProperty is KMutableProperty<*>)
重要区分:
KProperty<*>
:只读属性(对应val
)KMutableProperty<*>
:可变属性(对应var
)
Java Bean 兼容性
Kotlin 反射也能识别遵循 JavaBean 规范的 getter/setter,并将其视为“属性”。例如:
public class Person {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
在 Kotlin 中可通过 person::name
获取其属性引用。
Getter 与 Setter 操作
每个属性引用都暴露 getter
和 setter
(后者仅限 var
):
val prop = this::mutableProperty
assertEquals(String::class.starProjectedType, prop.getter.returnType)
prop.set("Hello")
assertEquals("Hello", prop.get())
prop.setter.call("World") // 等价于 setter("World")
assertEquals("World", prop.getter.call())
✅ 推荐使用 get()
/ set()
简化调用;若需传递上下文或做代理,则用 getter.call()
形式。
3.4 Kotlin 注解引用
注解是附加在类、方法、属性上的元数据。Kotlin 反射提供了统一的方式来读取它们。
先定义两个注解和一个被标注的类:
annotation class BaeldungExample(val url: String)
annotation class NotTested(val reason: String)
@NotTested("We trust developers ^_*")
@BaeldungExample("https://www.baeldung.com")
class BaeldungClass
查找特定注解
使用 findAnnotation<T>()
快速获取指定类型的注解实例:
val theClass = BaeldungClass::class
val notTestedAnnotation = theClass.findAnnotation<NotTested>()
assertNotNull(notTestedAnnotation)
assertEquals("We trust developers ^_*", notTestedAnnotation!!.reason)
✅ findAnnotation()
返回第一个匹配项或 null,适合大多数场景。
获取全部注解
通过 annotations
属性获取所有注解列表:
val annotations = theClass.annotations
assertEquals(2, annotations.size)
然后可通过类型过滤提取目标注解:
val baeldungAnnotation = theClass.annotations
.filterIsInstance<BaeldungExample>()
.first()
assertNotNull(baeldungAnnotation)
assertEquals("https://www.baeldung.com", baeldungAnnotation.url)
✅ filterIsInstance<T>()
是 Kotlin 标准库提供的安全类型过滤函数,在处理混合类型集合时非常实用。
⚠️ 踩坑提醒:确保注解声明为 @Retention(AnnotationRetention.RUNTIME)
,否则运行时无法通过反射访问!
4. 总结
本文系统介绍了 Kotlin 中的反射能力:
- ✅ Java 反射完全可用,适合简单场景或跨语言互操作
- ✅ Kotlin 自有反射 API 更强大,能感知 language features 如 data class、nullable、suspend 等
- ✅ 核心是
::
引用机制,支持类、方法、属性、注解的元数据访问 - ✅ 支持动态调用、实例创建、注解解析等高级操作
所有示例代码均已开源,可在 GitHub 查看完整项目:
👉 https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-advanced
📌 建议:日常开发优先考虑编译期方案(如注解处理器、KSP),反射作为兜底手段使用。生产环境注意性能监控与权限控制。