我有一个抽象类(MessageHandlerAdapter
),它带有一个通用类型(P
),然后用于键入传递给其函数之一的值:
abstract class MessageHandlerAdapter<in P : Any> {
abstract fun canHandle(): MessageType
abstract fun handle(payload: P)
}
如果我将其与List<MessageHandlerAdapter<*>>
一起使用,则会收到关于Out-projected type 'MessageHandlerAdapter<*>' prohibits the use of 'public abstract fun handle(payload: P): Unit defined in MessageHandlerAdapter'
的使用的错误消息strategy.handle(message.payload)
:
class MessageHandlerStrategy(
private val messageHandlers: List<MessageHandlerAdapter<*>>
) {
private val strategies = messageHandlers.map { Pair(it.canHandle(), it) }.toMap()
fun handle(message: Message<Any>) {
val strategy = strategies[message.type]
?: throw IllegalArgumentException>(IllegalArgumentException("No MessageHandler for message of type ${message.type}"))
strategy.handle(message.payload)
}
}
但是,如果我将其与List<MessageHandlerAdapter<Any>>
一起使用,则会在尝试传递的列表上收到错误消息Type inference failed. Expected type mismatch: required: List<MessageHandlerAdapter<Any>>found: List<MessageHandlerAdapter<*>>
:
lateinit var stringHandler: MessageHandlerAdapter<String>
lateinit var intHandler: MessageHandlerAdapter<Int>
val messageHandlerStrategy = MessageHandlerStrategy(listOf(stringHandler, intHandler))
class MessageHandlerStrategy(
private val messageHandlers: List<MessageHandlerAdapter<Any>>
) {
private val strategies = messageHandlers.map { Pair(it.canHandle(), it) }.toMap()
fun handle(message: Message<Any>) {
val strategy = strategies[message.type]
?: throw IllegalArgumentException>(IllegalArgumentException("No MessageHandler for message of type ${message.type}"))
strategy.handle(message.payload)
}
}
我还尝试将MessageHandlerAdapter
的签名更改为abstract class MessageHandlerAdapter<P : Any> {...
,但这没有任何区别。
答案 0 :(得分:1)
由于您有MessageHandlerAdapter
个不同类型的列表,因此列表必须使用星形投影<*>
,但这会使从列表中出来的对象对于调用任何将类型作为论点。您将必须转换检索到的适配器,以便能够在其上调用类型化的函数。
首先,我将消除MessageType类,而仅使用有效负载类型的KClass来简化我们的处理方式,并消除创建MessageType和有效负载类型不匹配的Message的可能性:
abstract class MessageHandlerAdapter<P : Any>(val payloadType: KClass<P>) {
abstract fun handle(payload: P)
}
// Just an example, don't know what your Message class has
data class Message<P : Any>(val payloadType: KClass<P>, val payload: P)
然后可以使用邮件的payloadType
属性进行轻松检查和投射。
在您的策略类中,您可以将MessageHandlerAdapter<*>
强制转换为MessageHandlerAdapter<P>
,并知道这样做是安全的,因为您是通过payloadType
对其进行检索的,而该try/catch
必须与其通用名称相同类型。
不幸的是,即使您使用@Suppress
,编译器也不足以了解这一点,因此您将得到未经检查的强制转换警告。您可以使用handle
隐藏警告。
Message<Any>
函数还需要通用的消息类型,否则您将只能传递class MessageHandlerStrategy(
private val messageHandlers: List<MessageHandlerAdapter<*>>
) {
fun <P : Any> handle(message: Message<P>) {
val strategy = messageHandlers.find { it.payloadType == message.payloadType }
?: throw IllegalArgumentException("No MessageHandler for message of type ${message.payloadType}")
@Suppress("UNCHECKED_CAST")
(strategy as MessageHandlerAdapter<Any>).handle(message.payload)
}
}
。
click_button('11 - teste')