Kotlin突出型禁止使用

时间:2018-11-01 00:16:17

标签: generics kotlin

我最近在Kotlin中尝试了以下方法。 我的想法是,我将收到扩展了 var identifier = ... // Some String identifying the bean var clazz = ... // Some class object coming from a dependency var beanDefinition = new RootBeanDefinition(); beanDefinition.setTargetType(clazz); beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON); beanDefinition.setAutowireCandidate(true); beanDefinition.setFactoryBeanName(CONTRACT_FACTORY_BEAN_NAME); beanDefinition.setFactoryMethodName(CONTRACT_FACTORY_METHOD_NAME); registry.registerBeanDefinition(identifier, beanDefinition); 的{​​{1}}(例如,像Item这样的输入)。 我正在尝试为这些项目中的每个项目使用不同的解析器 这是示例代码

AmericanItem

我的问题在最后一行,当我尝试调用解析器时,出现以下编译错误

BaseItem

我在这里做错了什么?

2 个答案:

答案 0 :(得分:3)

您在MapItemParser的声明之间创建了冲突。映射可以包含BaseItem的任何后代,但是ItemParser的设计是每个后代只能对BaseItem的后代中的一个进行操作。因此,对于ItemParser的给定实例,它必须接受它可以识别的内容,在这里您不能这样做,因为您的foundParser可以是任何后代,而不是给定{{1 }}实例。它应该猜哪个ItemParser?!?不能。

因此,您必须围绕基类而不是后代设计API。您使编译器无法知道将传递给T方法的内容。您唯一知道的一件事是它是一个parse()实例。

只有知道使用映射的诀窍,可以确保您使用正确的类型调用正确的实例。编译器不知道使您保证的逻辑。

我建议您更改API,以添加一个BaseItem方法供您执行其工作,并用一个通用的internalParse函数包装,该函数会仔细检查并进行邪恶转换。

parse

请注意,您也可以使用Kotlin类代替类型为abstract class BaseItem class AmericanItem : BaseItem() class EuropeanItem : BaseItem() interface ItemParser<T: BaseItem> { @Suppress("UNCHECKED_CAST") fun parse(item: BaseItem) { val tempItem = item as? T ?: throw IllegalArgumentException("Invalid type ${item.javaClass.name} passed to this parser") internalParse(tempItem) } fun internalParse(item: T) } class AmericanItemParser : ItemParser<AmericanItem> { override fun internalParse(item: AmericanItem) { println("AmericanItemParser") } } class EuropeanItemParser : ItemParser<EuropeanItem> { override fun internalParse(item: EuropeanItem) { println("parsing EuropeanItem") } } fun main(args: Array<String>) { val hashMap = HashMap<Class<out BaseItem>, ItemParser<*>>() hashMap.put(AmericanItem::class.java, EuropeanItemParser()) hashMap.put(EuropeanItem::class.java, AmericanItemParser()) val inputItem = EuropeanItem() val foundParser = hashMap[inputItem.javaClass] foundParser?.parse(inputItem) } 的Java类。

答案 1 :(得分:2)

ItemParser<*>是一个未知T而不是任何T的解析器。由于T是未知的,因此没有可以安全地传递给期望parse的函数T的值。

如果您确定地图包含给定ItemParser<T>键的Class<T>,则可以编写以下使用未经检查的强制转换的帮助函数:

fun <T : BaseItem> Map<Class<out T>, ItemParser<*>>.findParserFor(item: T) =
    get(item.javaClass) as ItemParser<T>?

然后像这样使用它:

val foundParser = hashMap.findParserFor(inputItem)
foundParser?.parse(inputItem)

请注意,在您的示例中,地图将AmericanItem::class.java键关联到EuropeanItemParser,反之亦然

hashMap.put(AmericanItem::class.java, EuropeanItemParser())
hashMap.put(EuropeanItem::class.java, AmericanItemParser())

由于运行时异常,代码将失败:

  

ClassCastException:EuropeanItem无法转换为AmericanItem

EuropeanItem传递给parse()函数而不是findParserFor函数时,将发生异常。发生这种情况是因为返回的ItemParser<EuropeanItem>实例实际上是AmericanItemParser。这是因为使用了一个未经检查的强制类型转换的结果,假设类型实际上是匹配的,而这实际上是不匹配的,这就是为什么将该强制转换类型称为“未经检查的”。