class 级别的 Kotlin 具体化类型

Kotlin reified type at class level

我正在尝试继承用于枚举的库 API。

abstract class SomeLibraryClass<T> {
    abstract fun test(key: String): T
}

class Item<T : Enum<T>> : SomeLibraryClass<T>() {
    override fun test(key: String): T {
        return enumValueOf(key)
    }
}

无法将 T 作为 reifiedtest 作为内联。如何为枚举继承库 class?

这是不可能的。必须在编译时知道具体化类型。这是通过将函数主体内联到调用站点来实现的,其中 T 是已知的。

参数化 class 不知道其 T,因此 T 不能用作具体化类型。

解决方案是在初始化Item时捕获Class<T>KClass<T>的实例,并将其保存在属性:

class Item<T : Enum<T>>(
    private val enumClass: KClass<T>
) : SomeLibraryClass<T>() {
    override fun test(key: String): T {
        return java.lang.Enum.valueOf(enumClass.java, key)
    }
}

更好的是,如果我们不打算扩展 Item,那么我们可以将构造函数设为内部并提供一个具体化的工厂函数来捕获 KClass/Class:

class Item<T : Enum<T>> @PublishedApi internal constructor(
    private val enumClass: KClass<T>
) : SomeLibraryClass<T>() {
    companion object {
        inline fun <reified T : Enum<T>> create() = Item(T::class)
    }

    override fun test(key: String): T {
        return java.lang.Enum.valueOf(enumClass.java, key)
    }
}

我们这样使用它:

val item = Item.create<Color>()
println(item.test("RED"))

java.lang.Enum.valueOf 远非理想,但 AFAIK 不幸的是,Kotlin 没有提供多平台和流畅的方式来从 KClass 获取枚举值。多年前就有人要求此功能:https://youtrack.jetbrains.com/issue/KT-14743,但仍未实现。

run-time 中不存在类型参数 T,这意味着您不能将其用作内联函数的具体化类型参数。 您需要将实际类型作为附加参数才能使用它,如下所示:

open class Item<T : Enum<T>>(
    private val type: KClass<T>,
) : SomeLibraryClass<T>() {
    override fun test(key: String): T {
        return type.java.enumConstants.find { it.name == key }
            ?: throw IllegalStateException("element with key $key not in enum")
    }
}

然后您可以像这样覆盖 class:

class MyItem : Item<MyEnum>(MyEnum::class)

正如其他人所建议的,这是不可能的。具体化类型参数目前仅适用于函数类型参数。 原因 为什么 reified 需要 inline 才能工作。 (另请参阅 ) There are also inline classes (value class)。他们对您的 class 施加了许多其他限制,并且它肯定不适用于您的情况,但我只想提一下语言 可以 被设计成允许这样的事情发生:

value class Item<reified T>(val wrapped: SomeLibraryClass<T>) {
     inline fun test(key: String) = enumValueOf(key)
}

也就是说,如果class函数都是内联的,那么在函数内部就可以知道T是什么。另请注意,您不能使用继承,因为那样会阻止 class.

的内联

但是 Kotlin 现在不支持这个,因为我想实现这个会使编译器变得更加复杂,而且它的用例非常有限。

无论如何,你可以存储一个KClass并使用反射:

class Item<T : Enum<T>>(private val enumClass: KClass<T>) : SomeLibraryClass<T>() {
    @Suppress("UNCHECKED_CAST")
    override fun test(key: String) =
        enumClass.staticFunctions.find { it.name == "valueOf" }?.call(key) as T
}

请注意,这很慢。