Kotlin仿制药无法正确投放

时间:2019-12-04 16:33:52

标签: generics kotlin casting

我有一个抽象类(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> {...,但这没有任何区别。

1 个答案:

答案 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')
相关问题