1. 概述

反射(Reflection)是指在运行时动态地检查、加载和调用类、字段、方法的能力,即使在编译期并不知道这些元素的具体信息。

这在实际开发中用途广泛,尤其是一些框架如 Spring 就重度依赖反射机制来实现依赖注入、AOP 等功能。JVM 原生支持反射,因此所有基于 JVM 的语言都可以使用。不过,像 Kotlin 这样的现代语言在此基础上提供了更高级的 API,让反射操作更简洁、类型更安全。

✅ 使用场景包括:

  • 动态创建对象
  • 序列化/反序列化框架
  • 注解处理器
  • 测试框架中的自动扫描与调用

⚠️ 踩坑提示:反射性能开销较大,且破坏封装性,应避免在高频路径滥用。


2. Java 反射在 Kotlin 中的使用

Kotlin 完全兼容标准 Java 反射 API,可以直接使用 java.lang.Classjava.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>,因此你可以直接使用 forEachmap 等函数式操作。

❌ 注意事项:虽然 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 原生的反射类型(如 KFunctionKProperty),比 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 操作

每个属性引用都暴露 gettersetter(后者仅限 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),反射作为兜底手段使用。生产环境注意性能监控与权限控制。


原始标题:Reflection with Kotlin