有没有办法从“String”转换为“KType”?

Rol*_*lie 3 kotlin

简单地说,我想要一个像这样的函数:

fun <T> convert(val foo: String, fooT: KType) : T {
    ...?
}
Run Code Online (Sandbox Code Playgroud)

对于Int,它会返回foo.toInt(),对于Doublefoo.toDouble(),并且对于某些未知类型,只是抛出异常。我认为为我期望的类型创建自己的 switch 语句并不难,但出于好奇 - 已经有办法了吗?

The*_*tor 5

推荐方式

不幸的是,没有简单的通用方法,因为我们处理的不是强制类型转换,而是方法调用。这就是我的方法:

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
}
Run Code Online (Sandbox Code Playgroud)

用法:

@UseExperimental(ExperimentalStdlibApi::class)
fun main() {

    val int = convert<Int>("32", typeOf<Int>())

    println("converted: $int")
}
Run Code Online (Sandbox Code Playgroud)

除了KType参数之外,您还可以使用 aClass<T>并使函数具体化,因此可以将其称为 asconvert<Int>("32")甚至"32".toGeneric<Int>()


硬核方式

虽然没有简单的方法,但可以使用大量反射并依赖实现细节来访问该类型。为此,我们可以从对象中提取类型名称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")
}
Run Code Online (Sandbox Code Playgroud)