我最近在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
我在这里做错了什么?
答案 0 :(得分:3)
您在Map
和ItemParser
的声明之间创建了冲突。映射可以包含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
。这是因为使用了一个未经检查的强制类型转换的结果,假设类型实际上是匹配的,而这实际上是不匹配的,这就是为什么将该强制转换类型称为“未经检查的”。