Рекомендуемый способ
К сожалению, нет простого универсального способа, потому что мы имеем дело не с приведениями, а с вызовами методов. Это мой подход:
fun <T> convert(str: String, type: KType) : T {
val result: Any = when (type.jvmErasure)
{
Long::class -> str.toLong()
Int::class -> str.toInt()
Short::class -> str.toShort()
Byte::class -> str.toByte()
...
else -> throw IllegalArgumentException("'$str' cannot be converted to $type")
}
return result as T // unchecked cast, but we know better than compiler
}
Использование:
@UseExperimental(ExperimentalStdlibApi::class)
fun main() {
val int = convert<Int>("32", typeOf<Int>())
println("converted: $int")
}
Вместо параметра KType
вы также можете использовать Class<T>
и сделать функцию reified, так чтоможно назвать как convert<Int>("32")
или даже "32".toGeneric<Int>()
.
Hardcore way
Пока нет easy way, он is возможно получить доступ к типу, используя сильное отражение и полагаясь на детали реализации. Для этого мы можем извлечь имя типа из объекта KType
, найти соответствующий метод расширения (в другом классе) и вызвать его с помощью отражения.
Мы должны использовать to*OrNull()
вместо to*()
, потому что последний встроен и не будет найден отражением. Кроме того, нам нужно прибегнуть к рефлексии Java - в настоящее время рефлексия Kotlin генерирует UnsupportedOperationException
для задействованных типов.
Я не рекомендую это в производительном коде, поскольку он неэффективен и может нарушить будущий стандартверсии библиотеки, но это хороший эксперимент:
fun convert(str: String, type: KType): Any {
val conversionClass = Class.forName("kotlin.text.StringsKt")
// here, the to*OrNull() methods are stored
// we effectively look for static method StringsKt.to*OrNull(String)
val typeName = type.jvmErasure.simpleName
val funcName = "to${typeName}OrNull" // those are not inline
val func = try {
conversionClass.getMethod(funcName, String::class.java) // Java lookup
} catch (e: NoSuchMethodException) {
throw IllegalArgumentException("Type $type is not a valid string conversion target")
}
func.isAccessible = true // make sure we can call it
return func.invoke(null, str) // call it (null -> static method)
?: throw IllegalArgumentException("'$str' cannot be parsed to type $type")
}