1. 概述

在本教程中,我们将介绍几种通过反射和 KClass 类型令牌来创建 Kotlin 类实例的方法。这在某些需要动态实例化的场景下非常有用,比如依赖注入、序列化/反序列化等。

我们将会用到 Kotlin 的反射模块 kotlin-reflect,并演示如何处理不同类型的构造函数:无参构造函数、含可选参数的构造函数、以及含必填参数的构造函数。

2. 依赖配置

为了使用 KClass 和反射功能,我们需要引入 kotlin-reflect 模块。如果你使用 Maven 构建项目,可以在 pom.xml 中添加如下依赖:

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-reflect</artifactId>
    <version>1.5.21</version>
</dependency>

我们还会用到以下三个用于测试的类:

class NoArg
class OptionalArgs(val arg: String = "default")
class RequiredArgs(val arg1: String, val arg2: String) {
    constructor(arg1: String): this(arg1, "default")
}

其中:

  • NoArg 类有一个无参构造函数 ✅
  • OptionalArgs 有一个带默认值的可选参数 ❗️
  • RequiredArgs 有两个必填参数,并且还有一个只带一个参数的次级构造函数 ⚠️

3. 反射创建实例

从 Kotlin 1.1 开始,KClass 提供了一个扩展方法 createInstance(),可以用于通过反射创建类的实例:

val noArgInstance = NoArg::class.createInstance()
assertNotNull(noArgInstance)
assertThat(noArgInstance).isInstanceOf(NoArg::class.java)

✅ 该方法仅适用于无参构造函数或所有参数都有默认值的构造函数。因此,也可以用于 OptionalArgs

val instance = OptionalArgs::class.createInstance()
assertNotNull(instance)
assertThat(instance).isInstanceOf(OptionalArgs::class.java)

❌ 但如果尝试用它来创建含有必填构造参数的类(如 RequiredArgs),就会抛出异常:

val exception = assertThrows<IllegalArgumentException> { RequiredArgs::class.createInstance() }
assertThat(exception).hasMessageStartingWith("Class should have a single no-arg constructor")

这是因为 createInstance() 要求类必须有无参构造函数。否则就会抛出 IllegalArgumentException

3.1 使用主构造函数

对于有必填参数的类,我们可以直接获取其主构造函数并调用它:

val primaryConstructor = RequiredArgs::class.primaryConstructor
val instance = primaryConstructor!!.call("first arg", "second arg")
assertNotNull(instance)
assertThat(instance).isInstanceOf(RequiredArgs::class.java)
  • primaryConstructor 是一个扩展属性,用来获取类的主构造函数 ✅
  • call() 方法用于执行构造函数并传入参数 ❗️

3.2 使用次级构造函数

当一个类有多个构造函数时,可以通过 constructors 属性获取所有构造函数,并选择合适的那个来调用:

val constructors = RequiredArgs::class.constructors
val instance = constructors.first { it.parameters.size == 1 }.call("arg1")
assertEquals("arg1", instance.arg1)
assertEquals("default", instance.arg2)

上面的例子中我们:

  1. 获取所有构造函数
  2. 找到参数个数为 1 的那个
  3. 调用该构造函数并传入 "arg1"

这样就可以成功调用次级构造函数创建实例。

4. 小结

在本文中,我们学习了如何通过 KClass 反射创建 Kotlin 类的实例,包括:

  • ✅ 使用 createInstance() 创建无参或默认参数类的实例
  • ✅ 使用 primaryConstructorcall() 创建主构造函数需要参数的实例
  • ✅ 使用 constructors 属性查找并调用合适的次级构造函数

这些方法在动态创建对象、依赖注入框架、插件系统等场景中非常实用。

所有示例代码都可以在 GitHub 仓库 中找到。


原始标题:Creating a New Instance from a KClass in Kotlin