Как вызвать метод фабрики компаньонов Kotlin, используя callBy()?
У меня есть код принимает класс в качестве параметра и подготавливает данные для вызова конструктора для этого класса метода фабрики сопутствующих объектов, если присутствует фабричный метод.
Все работает нормально при вызове конструктора, но я получаю ошибку
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 ответа
Если вы позвоните ::fromJson.parameters
вы получите список с 3 KParameter
s. Первый из них является экземпляром объекта-компаньона.
Итак, вот список параметров, которые вы должны предоставить.
Вы можете вызвать fromJson
как это:
val params = ::fromJson.parameters
// not using any default parameters
::fromJson.callBy(mapOf(
params[0] to StudentWithFactory::class.companionObjectInstance,
params[1] to "Hello",
params[2] to 30
))
// using only the default parameter for "name"
::fromJson.callBy(mapOf(
params[0] to StudentWithFactory::class.companionObjectInstance,
params[2] to 30
))
В дополнение к моему короткому ответу, более техническое объяснение:
Да, на самом деле есть скрытый параметр, и вы можете увидеть его (например), если взгляните на декомпилированный (в 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)
* оставил код для простоты