将`Type`转换为`KType`

问题描述 投票:0回答:1

有没有办法从

KType
获取 Kotlin
java.lang.reflect.Type

背景:我正在编写一些代码,这些代码从类中获取属性/字段并将它们传递给方法(对于熟悉这些理论的人来说,请考虑 JUnit 4 理论)。因为我需要 Java 兼容性,所以我不能总是使用纯 Kotlin 反射,因为像

MyClass::class.memberProperties
这样的调用不适用于原始 Java 字段。

另一种选择是将我的

KType
转换为
java.lang.reflect.Type
(通过
.javaType
),但是
KType
提供了一个方便的
isSupertypeOf(...)
函数,允许在两个
KTypes
之间进行比较,我还没有找到类似的Java 的功能
Type
.

非常粗略的示例:

Kotlin 代码:

class KotlinTestClass {
    fun test(someParam: List<String>) {
        someParam.forEach { println(it) }
    }

    class CompatibleTypes {
        val exact: List<String> = listOf("foo")
        val mutable: MutableList<String> = mutableListOf("bar")
        val implementation: ImmutableList<String> = immutableListOf("baz")
    }

    class NonCompatibleTypes {
        val wrongParam: List<Int> = listOf(42)
        val wrongType: Map<String, String> = mapOf("hello" to "world")
    }
}

Java代码:

public class JavaTestClass {
    public static class CompatibleTypes {
        public static final List<String> exact = List.of("foo");
        public static final ImmutableList<String> implementation = ImmutableList.of("baz");
    }

    public static class NonCompatibleTypes {
        public static final List<Integer> wrongParam = List.of(42);
        public static final Map<String,String> wrongType = Map.of("hello", "world");
    }
}

Kotlin 测试代码:

fun main(args: Array<String>) {
    val parameterType = KotlinTestClass::class.functions.first { it.name == "test" }.parameters[1].type

    println("Target: $parameterType")

    println("--------------------------------------")
    //These should all print "true"
    println("Compatible Kotlin types:")
    KotlinTestClass.CompatibleTypes::class.memberProperties.asSequence()
        .map { it.returnType }
        .forEach { println(" - ${parameterType.isSupertypeOf(it)}  ($it)") }

    println("--------------------------------------")
    println("Compatible Java types:")
    //These should all print "true"
    //This code doesn't work because the fields aren't valid Kotlin properties
    JavaTestClass.CompatibleTypes::class.memberProperties.forEach { println(it) }
    JavaTestClass.CompatibleTypes::class.java.declaredFields.asSequence()
        .map { it.kotlinProperty?.returnType }
        .filterNotNull()
        .forEach { println(" - ${parameterType.isSupertypeOf(it)}  ($it)") }

    println("--------------------------------------")
    println("Non-Compatible Kotlin types:")
    //These should all print "false"
    KotlinTestClass.NonCompatibleTypes::class.memberProperties.asSequence()
        .map { it.returnType }
        .forEach { println(" - ${parameterType.isSupertypeOf(it)}  ($it)") }

    println("--------------------------------------")
    println("Non-Compatible Java types:")
    //These should all print "false"
    //This code doesn't work because the fields aren't valid Kotlin properties
    JavaTestClass.NonCompatibleTypes::class.java.declaredFields.asSequence()
        .map { it.kotlinProperty?.returnType }
        .filterNotNull()
        .forEach { println(" - ${parameterType.isSupertypeOf(it)}  ($it)") }
}
kotlin reflection kotlin-reflect
1个回答
0
投票

假设

Type
来自 Java 中的声明,以下适用于简单类、参数化类型、原始类型和数组类型。不过,可能有一些我没有处理的边缘情况。

这个想法基本上是检查

Type
可能是什么(
Class
ParameterizedType
TypeVariable
等)并处理每种情况,并在需要时递归遍历“类型树”。

fun convert(type: Type) = when(type) {
    is Class<*> -> when {
        // we takeUnless it.isPrimitive here because primitve arrays are mapped to their own Kotlin classes, not Array<T>
        type.isArray -> type.kotlin.createType(listOfNotNull(type.componentType?.takeUnless { it.isPrimitive }?.toKTypeProjection()))
        else -> type.kotlin.createType(List(type.typeParameters.size) { KTypeProjection.STAR })
    }
    is ParameterizedType -> (type.rawType as Class<*>).kotlin.createType(
        type.actualTypeArguments.map { it.toKTypeProjection() }
    )
    is GenericArrayType -> Array::class.createType(listOf(type.genericComponentType.toKTypeProjection()))
    is TypeVariable<*> -> type.toKType()
    else -> throw NotImplementedError()
}

fun Type.toKTypeProjection(): KTypeProjection = when (this) {
    is WildcardType -> when {
        // lowerBounds and upperBounds should contain at most one element in Java
        lowerBounds.isNotEmpty() -> KTypeProjection(KVariance.IN, convert(lowerBounds[0]))
        upperBounds.isNotEmpty() -> KTypeProjection(KVariance.OUT, convert(upperBounds[0]))
        else -> KTypeProjection(KVariance.INVARIANT, convert(this))
    }
    else -> KTypeProjection(KVariance.INVARIANT, convert(this))
}

fun TypeVariable<*>.toKType() = when (val decl = this.genericDeclaration) {
    is Class<*> -> decl.kotlin.typeParameters.first { it.name == this.name }.createType()
    is Method -> decl.kotlinFunction!!.typeParameters.first { it.name == this.name }.createType()
    else -> throw NotImplementedError()
}
© www.soinside.com 2019 - 2024. All rights reserved.