【问题标题】:Kotlin data class instanciation via reflection通过反射实现 Kotlin 数据类实例化
【发布时间】:2019-07-27 11:38:23
【问题描述】:

我正在为连接到 MongoDB 的应用程序编写一个小型数据访问层,我想使用反射自动将查询的实体实例化为(域模型)数据类

我看到人们谈论将 JSON 反序列化为 Kotlin 数据类,我明白为什么由于 MongoDB 以相关的 BSON 格式返回数据,这将是一个选项。但是,我想对那个数据源进行抽象。

所以给定一个任意类型的字段名称和值的映射,我如何定义一个函数toDomain,以便它将这样的映射和目标类Class<T> 作为参数并吐出T 作为结果?我被卡住了,因为newInstance() 方法调用了(不存在的)默认构造函数,并且我的数据类构造函数参数的数量和类型各不相同,所以我也无法“硬编码”这些信息。

编辑:我想澄清一下,我不想知道任何我可以简单地插入我的代码的库(除非它们是开源的并且具有可读的实现),而是我想了解我拥有的可能性有这方面的思考。

【问题讨论】:

  • 你能用newInstance()方法添加一个示例代码吗?我的意思是具有该方法的类...
  • @BartekLipinski 目前我接受clazz: Class<T> 作为参数,其中 T 是任意域模型数据类。但是数据类必须至少有一个构造函数参数,所以clazz.newInstance() 失败,因为没有默认构造函数。

标签: generics kotlin reflection


【解决方案1】:

我找到了一个有点实用的解决方案。编译时,我在 Kotlin gradle 构建脚本中提供了 -parameters 标志:

compileKotlin.kotlinOptions.javaParameters = true

那么下面的代码就可以工作了:

fun <T> Document.asInstanceOf(clazz: Class<T>): T {
    val constructor = clazz.constructors.first()
    val args = mutableListOf<Any>()
    for (param in constructor.parameters) {
        args.add(this[param, param.type])
    }
    @Suppress("UNCHECKED_CAST")
    return constructor.newInstance(*args.toTypedArray()) as T
}

因为运行时知道我的构造函数参数的名称,因此我可以进行直接映射。这不是最终版本,只是一个小例子。

【讨论】:

    【解决方案2】:

    您可以使用KClass&lt;T&gt;(如果您从其他代码中获得Class&lt;T&gt;,clazz.kotlin 会为您提供KClass)。它包含您需要的信息,应该类似于(根据您自己的答案)

    fun <T> Document.asInstanceOf(clazz: KClass<T>): T {
        val constructor = clazz.primaryConstructor!!
        val args = constructor.parameters.map { param ->
            this[param.name, param.type.jvmErasure.java]
        }
        constructor.call(*args)
    }
    

    您需要将kotlin-reflect 添加到您的依赖项中才能使用它(不是KClass 本身,而是它的大部分功能)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-23
      • 2020-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多