在尝试使用嵌套泛型将Java代码转换为Kotlin时,我遇到了一些麻烦。以Java SSCCE为例(请注意S和T之间的关系):
public class JavaTest {
private class JavaObjectContainer<S> {
public S obj;
}
private abstract class JavaSampleClass<S, T extends JavaObjectContainer<S>> {
private Class<S> type;
public JavaSampleClass(Class<S> type) {
this.type = type;
}
public Class<S> getType() {
return type;
}
public abstract void callMethod(S s);
}
private class JavaChildSampleClass extends JavaSampleClass<String, JavaObjectContainer<String>> {
public JavaChildSampleClass() {
super(String.class);
}
@Override
public void callMethod(String s) {}
}
private class JavaTestContainer {
private Map<Class<?>, JavaSampleClass> sampleClasses;
public JavaTestContainer() {
this.sampleClasses = new HashMap<>();
}
public void registerJavaSampleClass(JavaSampleClass javaSampleClass) {
sampleClasses.put(javaSampleClass.getType(), javaSampleClass);
}
public void callMethod(Object obj) {
sampleClasses.get(obj.getClass()).callMethod(obj);
}
}
public void test() {
JavaTestContainer javaTestContainer = new JavaTestContainer();
javaTestContainer.registerJavaSampleClass(new JavaChildSampleClass());
javaTestContainer.callMethod("Hola");
}
}
将此SSCCE视为通用工厂模式的实现,其中用户注册多个JavaSampleClass,其方法可在将来调用。
由于Kotlin没有提供通配符的替代方法,我尝试了以下方法:
class KotlinTest {
private class KotlinObjectContainer<S> {
var obj : S? = null
}
private open class KotlinSampleClass<S, T : KotlinObjectContainer<S>>(var type: Class<S>) {
fun callMethod(s : S) {}
}
private class KotlinChildSampleClass : KotlinSampleClass<String, KotlinObjectContainer<String>>(String::class.java)
private inner class KotlinTestContainer {
private val sampleClasses: MutableMap<Class<Any>, KotlinSampleClass<Any, KotlinObjectContainer<Any>>> = mutableMapOf()
fun registerKotlinSampleClass(kotlinSampleClass: KotlinSampleClass<Any, KotlinObjectContainer<Any>>) {
sampleClasses.put(kotlinSampleClass.type, kotlinSampleClass)
}
fun callMethod(obj : Any) {
sampleClasses[obj.javaClass]?.callMethod(obj)
}
}
fun test() {
val kotlinTestContainer = KotlinTestContainer()
// Exception!
kotlinTestContainer.registerKotlinSampleClass(KotlinChildSampleClass())
kotlinTestContainer.callMethod("Hello")
}
}
以上代码在IDE中抛出以下异常:
Type mismatch.
Required: KotlinTest.KotlinSampleClass<Any, KotlinObjectContainer<Any>>
Found: KotlinTest.KotlinChildSampleClass
我一直在考虑将sampleClasses映射声明为
MutableMap<*, *>
但是,我怎样才能初始化它?此外,由于*表示超出预测的参数,因此IDE在尝试将新值放入地图时会显示错误。
我如何克服这个问题?我很确定我错过了什么......
答案 0 :(得分:1)
由于Kotlin没有提供通配符的替代方案......
我一直在考虑将sampleClasses映射声明为
MutableMap<*, *>
对于这种情况,*
完全对应于通配符。如果您在Java中使用Class<?>
,则需要在Kotlin中Class<*>
,而不是Class<Any>
或*
。
private val sampleClasses: MutableMap<Class<*>, KotlinSampleClass<*, *>> = mutableMapOf()
fun registerKotlinSampleClass(kotlinSampleClass: KotlinSampleClass<*, *>) {
sampleClasses.put(kotlinSampleClass.type, kotlinSampleClass)
}
@Suppress("UNCHECKED_CAST")
fun callMethod(obj : Any) {
(sampleClasses[obj.javaClass] as KotlinSampleClass<Any, *>?)?.callMethod(obj)
}
你不需要在Java callMethod
中进行强制转换的唯一原因是因为你正在使用原始类型(如图灵85的评论提及),编译器基本上放弃了类型检查。