Kotlin 将子 class 类型转换为父 class 类型
Kotlin put children class type into parent class type
我正在尝试创建一个 object
来收集 Message
class 的子 class 的听众。
typealias Listener <T> = (T) -> Unit
object MessageRegistry {
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<Message>>>()
fun <T : Message> listen(clazz: KClass<T>, listener: Listener<T>) {
listeners.getOrPut(clazz) { mutableSetOf() }.add(listener)
}
}
当 ConnectedMessage
是 Message
的子类型时,我希望此对象的客户端像下面这样注册他们的侦听器:
MessageRegistry.listen(ConnectedMessage::class) { connectedMessage: ConnectedMessage ->
doSomething(connectedMessage)
}
不过,MessageRegistry.listen()
方法好像有问题。
我无法将 listener
添加到 MutableSet<Listener<Message>>>
。
listener
有一个类型 (T) -> Unit
,泛型 T
是 <T: Message>
和
我想添加到类型为 (Message) -> Unit
的 MutableSet
。
我收到如下类型错误:
Type mismatch.
Required:
Listener<Message> /* = (Message) → Unit */
Found:
Listener<T> /* = (T) → Unit */
正如我提到的 T
继承了 Message
class (<T : Message>
),我认为这是一个有效的转换。
我也试过将 out
标记为 Listener
的通用类型:
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<out Message>>>()
但它给出了以下错误:
Conflicting projection in type alias expansion in intermediate type '(T) -> Unit'
我该怎么做才能让听众在一个集合中?
提前致谢。
你的 Listener 是一个 T 消费者,而不是生产者,所以它不能被赋予 out Message
类型并且仍然有用。
由于您的函数允许 Listener 的 Message 子类型,但 Listener 是消费者,因此您的 Listener 无法满足成为 Listener<Message>
的要求。例如,如果您有一个 Listener<SubMessage>
并试图将一个 Message
传递给它,这将是不安全的,因为 Message
可能不是 SubMessage
.
但是,由于您将这些存储在具有 class 的 Map 中,您实际上是在创建 runtime-safety 类型,并且您可以从逻辑上推断类型如果匹配则可以安全转换.
因此,泛型系统无法保证安全,但如果您注意投射位置,则可以。你可以这样实现它:
object MessageRegistry {
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<*>>>()
fun <T : Message> listen(clazz: KClass<T>, listener: Listener<T>) {
listeners.getOrPut(clazz) { mutableSetOf() }.add(listener)
}
private fun <T: Message> getListeners(clazz: KClass<T>): MutableSet<Listener<T>> {
@Suppress("UNCHECKED_CAST")
return listeners[clazz] as MutableSet<Listener<T>>
}
}
我建议制作这些 inline/reified 以使调用站点的代码更简单。在大多数情况下,它将能够在调用站点推断类型,因此您不必显式传递它。
object MessageRegistry {
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<*>>>()
@PublishedApi
internal fun <T : Message> listen(clazz: KClass<T>, listener: Listener<T>) {
listeners.getOrPut(clazz) { mutableSetOf() }.add(listener)
}
inline fun <reified T : Message> listen(noinline listener: Listener<T>) {
listen(T::class, listener)
}
private fun <T: Message> getListeners(clazz: KClass<T>): MutableSet<Listener<T>> {
@Suppress("UNCHECKED_CAST")
return listeners[clazz] as MutableSet<Listener<T>>
}
}
我正在尝试创建一个 object
来收集 Message
class 的子 class 的听众。
typealias Listener <T> = (T) -> Unit
object MessageRegistry {
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<Message>>>()
fun <T : Message> listen(clazz: KClass<T>, listener: Listener<T>) {
listeners.getOrPut(clazz) { mutableSetOf() }.add(listener)
}
}
当 ConnectedMessage
是 Message
的子类型时,我希望此对象的客户端像下面这样注册他们的侦听器:
MessageRegistry.listen(ConnectedMessage::class) { connectedMessage: ConnectedMessage ->
doSomething(connectedMessage)
}
不过,MessageRegistry.listen()
方法好像有问题。
我无法将 listener
添加到 MutableSet<Listener<Message>>>
。
listener
有一个类型 (T) -> Unit
,泛型 T
是 <T: Message>
和
我想添加到类型为 (Message) -> Unit
的 MutableSet
。
我收到如下类型错误:
Type mismatch.
Required:
Listener<Message> /* = (Message) → Unit */
Found:
Listener<T> /* = (T) → Unit */
正如我提到的 T
继承了 Message
class (<T : Message>
),我认为这是一个有效的转换。
我也试过将 out
标记为 Listener
的通用类型:
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<out Message>>>()
但它给出了以下错误:
Conflicting projection in type alias expansion in intermediate type '(T) -> Unit'
我该怎么做才能让听众在一个集合中?
提前致谢。
你的 Listener 是一个 T 消费者,而不是生产者,所以它不能被赋予 out Message
类型并且仍然有用。
由于您的函数允许 Listener 的 Message 子类型,但 Listener 是消费者,因此您的 Listener 无法满足成为 Listener<Message>
的要求。例如,如果您有一个 Listener<SubMessage>
并试图将一个 Message
传递给它,这将是不安全的,因为 Message
可能不是 SubMessage
.
但是,由于您将这些存储在具有 class 的 Map 中,您实际上是在创建 runtime-safety 类型,并且您可以从逻辑上推断类型如果匹配则可以安全转换.
因此,泛型系统无法保证安全,但如果您注意投射位置,则可以。你可以这样实现它:
object MessageRegistry {
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<*>>>()
fun <T : Message> listen(clazz: KClass<T>, listener: Listener<T>) {
listeners.getOrPut(clazz) { mutableSetOf() }.add(listener)
}
private fun <T: Message> getListeners(clazz: KClass<T>): MutableSet<Listener<T>> {
@Suppress("UNCHECKED_CAST")
return listeners[clazz] as MutableSet<Listener<T>>
}
}
我建议制作这些 inline/reified 以使调用站点的代码更简单。在大多数情况下,它将能够在调用站点推断类型,因此您不必显式传递它。
object MessageRegistry {
private val listeners = mutableMapOf<KClass<out Message>, MutableSet<Listener<*>>>()
@PublishedApi
internal fun <T : Message> listen(clazz: KClass<T>, listener: Listener<T>) {
listeners.getOrPut(clazz) { mutableSetOf() }.add(listener)
}
inline fun <reified T : Message> listen(noinline listener: Listener<T>) {
listen(T::class, listener)
}
private fun <T: Message> getListeners(clazz: KClass<T>): MutableSet<Listener<T>> {
@Suppress("UNCHECKED_CAST")
return listeners[clazz] as MutableSet<Listener<T>>
}
}