mutableMap中泛型类的强制转换



在我的应用程序中,我想做这样的事情:

interface Binder<in VB: ViewBinding, in T: Any> {
fun bind(binding: VB, item: T)
}
class TypeInfoMap {
val map = mutableMapOf<Class<out Any>, Binder<ViewBinding, Any>>()
inline fun <reified VB: ViewBinding, reified T: Any> put(binder: Binder<VB, T>) {
map[T::class.java] = binder as Binder<ViewBinding, Any> // warning unchecked cast
}
inline fun <reified VB: ViewBinding, reified T: Any> get(cls: Class<T>): Binder<VB, T> {
return map[cls] as? Binder<VB, T> ?: throw IllegalStateException() // no warning
}
}

我在put函数中得到了警告unchecked cast。为什么?我为泛型类型声明了上限,这里的强制转换不是很好吗?此外,get函数中的强制转换不会产生任何警告,即使我没有内联该函数。我本以为我会在这里得到警告,但实际上我很惊讶我没有得到警告。

Kotlin有没有办法在没有警告的情况下写下这一切?我想这就是reified的作用。

首先,从Binder<VB, T>强制转换为Binder<ViewBinding, Any>是不正确的。如果将binder定义为Binder<VB, T>,则可以使用VB实例调用binder.bind(),但不能使用不是VBs的ViewBinding实例。因此,Binder<VB, T>不是Binder<ViewBinding, Any>

其次,unchecked cast警告与强制转换是否有效无关。如果类型不正确,那么在运行时就不会得到ClassCastException。这就是为什么它很危险。

get()方法中,您可能不会得到未检查的强制转换警告,因为强制转换始终有效:给定Binder的方差以及声明的父类型VBTBinder<ViewBinding, Any>始终是Binder<VB, T>

在Kotlin有没有一种方法可以在没有警告的情况下写下所有这些?我想这就是具体化的意义。

reified允许在运行时访问泛型类型,但只能访问具体化函数的泛型类型。例如,它们允许你在不显式传递的情况下获得某个实例的KClass。然而,你正在使用的内部映射在运行时仍然不会提供你过去在其中放入的信息。

你能做的最好的事情就是忽略未检查的强制转换警告,因为你在运行时无法知道映射中包含的泛型类型。然而,如果您将map设为私有,则您有一种更安全的方法,因为您可以控制其中的内容:

class TypeInfoMap {
private val map = mutableMapOf<Key<*, *>, Binder<*, *>>()
class Key<VB : ViewBinding, T : Any>(
val bindingClass: KClass<VB>,
val valueClass: KClass<T>,
)
fun <VB : ViewBinding, T : Any> put(key: Key<VB, T>, binder: Binder<VB, T>) {
map[key] = binder
}
@Suppress("UNCHECKED_CAST") // types are guaranteed by put()
fun <VB : ViewBinding, T : Any> get(key: Key<VB, T>): Binder<VB, T> {
val binder = map[key] ?: error("No binding of type ${key.bindingClass} found for class ${key.valueClass}")
return binder as Binder<VB, T>
}
inline fun <reified VB: ViewBinding, reified T: Any> put(binder: Binder<VB, T>) {
put(Key(VB::class, T::class), binder)
}
inline fun <reified VB: ViewBinding, reified T: Any> get(): Binder<VB, T> = get(Key(VB::class, T::class))
}

然后,您可以安全地将其与漂亮的具体化类型一起使用:

val infoMap = TypeInfoMap()
val someBinder: Binder<MyViewBinding, MyType> = createSomeBinderSomewhere()
infoMap.put(someBinder)
// guaranteed type here (or runtime error if no binding of those types is found)
val binder = infoMap.get<MyViewBinding, MyType>()

最新更新