Как вызвать метод фабрики компаньонов Kotlin, используя callBy ()? - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть код принимает класс в качестве параметра и подготавливает данные для вызова конструктора для этого класса метода фабрики сопутствующих объектов, если присутствует фабричный метод.

Все прекрасно работает при вызове конструктора,но я получаю ошибку

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 голосов
/ 23 февраля 2019

В дополнение к моему короткому ответу , есть более техническое объяснение:

Да, на самом деле есть скрытый параметр, и вы можете увидеть его (например), если вы посмотритев декомпилированном (в 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.Но импортированная часть заключается в том, что вам нужно беспокоиться только о var0, var1 и var2, если вы хотите вызвать функцию.

Итак, в конце концов, нестатическая версия fromJson *фактически вызывается для экземпляра объекта-компаньона:

var0.fromJson(var1, var2)

* оставил код для простоты

0 голосов
/ 23 февраля 2019

Вы можете использовать свойство parameters, чтобы определить, сколько параметров вы должны передать функции / конструктору.

Если вы вызовете

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

*, то 1007 * будет иметьразмер два, как и ожидалось, но если вы позвоните

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
))
...