Kotlin vs Java嵌套泛型

时间:2018-04-16 15:08:31

标签: java android generics kotlin

在尝试使用嵌套泛型将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在尝试将新值放入地图时会显示错误。

我如何克服这个问题?我很确定我错过了什么......

1 个答案:

答案 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的评论提及),编译器基本上放弃了类型检查。