Есть ли способ привести из «String» в «KType»? - PullRequest
1 голос
/ 07 ноября 2019

Проще говоря, я хочу такую ​​функцию, как:

fun <T> convert(val foo: String, fooT: KType) : T {
    ...?
}

Для Int, она вернет foo.toInt(), для Double, foo.toDouble() и в какой-то неизвестный тип, просто броситьисключение. Я думаю, что не так сложно создать свой собственный оператор switch для ожидаемых типов, но из любопытства - уже есть способ?

1 Ответ

2 голосов
/ 07 ноября 2019

Рекомендуемый способ

К сожалению, нет простого универсального способа, потому что мы имеем дело не с приведениями, а с вызовами методов. Это мой подход:

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")
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...