如何使用callBy()调用Kotlin伴侣工厂方法?

时间:2019-02-22 10:44:32

标签: reflection kotlin kotlin-companion

我有代码接受一个类作为参数,并准备数据以调用伴随对象工厂方法的该类的构造函数(如果存在工厂方法的话)。

调用构造函数时,一切正常,但出现错误

java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun nz.salect.objjson.JVMTest.StudentWithFactory.Companion.fromJson(kotlin.String, kotlin.Int): nz.salect.objjson.JVMTest.StudentWithFactory

在调用工厂方法时。有问题的工厂方法:

data class StudentWithFactory(val name: String, val years: Int=0) {

    companion object {

        fun fromJson(name: String="", age: Int = 0):StudentWithFactory {
            return StudentWithFactory(name, age)
        }
    }
}

没有必需的参数,除非有一些隐藏参数。有什么想法吗?

实际上,我还原了从fromJson完全删除参数并使用::fromJson.callby(emptyMap())直接调用伴随方法的情况。同样的错误。

很明显,伴随方法至少需要一个附加参数。也许上课?还是同伴对象? 如何指定所需的参数?

构建callBy()的函数提供了一个类(或从提供的类中找到该类)以及json名称和值。

var funk:KFunction<*>?=null
val companionFuncs=cls.companionObject?.declaredMemberFunctions
if(companionFuncs?.size ?:0 >0){
    companionFuncs?.forEach {
        if(it.name == "fromJson") funk=it
    }

}
val cons:KFunction<T> = if(funk != null)
       funk as KFunction<T>
    else
       cls.primaryConstructor ?: throw IllegalArgumentException("no primary constructor ${cls.simpleName}")
val valuesMap = cons.parameters.filter{it.name in vals}
    .associateBy(
    {it},
    {generateValue(it)}
)
val data = cons.callBy(valuesMap) //as T
return data

2 个答案:

答案 0 :(得分:0)

您可以使用parameters属性来确定必须向函数/构造函数传递多少参数。

如果您致电

val paramsConstr = StudentWithFactory::class.primaryConstructor?.parameters

paramsConstr的大小将为预期的两倍,但如果您致电

val paramsFunc = ::fromJson.parameters

paramsFunc的大小为三。第一个元素对应于伴随对象的实例。因此,这就是您需要提供的参数列表。

您可以像这样调用fromJson

// not using any default parameters
::fromJson.callBy(mapOf(
        paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
        paramsFunc[1] to "Hello",
        paramsFunc[2] to 30
))

// using only the default parameter for "name"
::fromJson.callBy(mapOf(
        paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
        paramsFunc[2] to 30
))

答案 1 :(得分:0)

除了我的short answer外,还有更多技术性解释:

是的,如果您查看反编译(到Java)的字节码,实际上就有一个隐藏的参数,您可以看到它(例如):

public final class StudentWithFactory {

   // ...
   public static final class Companion {
      // ...
      @NotNull
      public static StudentWithFactory fromJson$default(StudentWithFactory.Companion var0, String var1, int var2, int var3, Object var4) {
         // ...

         return var0.fromJson(var1, var2);
      }
      // ...
   }
}

第一个参数(var0)实际上是伴随对象的一个​​实例。 var1(名称)和var2(年龄)是您声明的参数。 var3是一个位掩码,用于确定是否已传递显式值或是否应使用默认值*。老实说,我不知道var4是干什么的。在Java代码中未使用。但是导入的部分是,如果要调用该函数,则只需要担心var0var1var2

因此,最后,实际上会在伴随对象的实例上调用fromJson *的非静态版本:

var0.fromJson(var1, var2)

*为简化起见,省略了代码