【问题标题】:Kotlin vs Java nested genericsKotlin vs Java 嵌套泛型
【发布时间】:2018-04-16 15:08:31
【问题描述】:

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

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

【问题讨论】:

  • 可能不相关,但仍然有代码味道:Don't use raw types.
  • 如果我没记错的话,使用原始类型基本上是使这种方法在 Java 中如此强大的原因。我已经为多类型 RecyclerView.Adapter 实现了一些类似的东西。这只是一个谨慎的问题,并添加一层保护以确保您尝试访问已添加的工厂。无论如何,我会研究你的观察,谢谢!

标签: java android generics kotlin


【解决方案1】:

由于 Kotlin 没有提供通配符的替代方案...

我一直在考虑将 sampleClasses 映射声明为

MutableMap<*, *>

对于这种情况,* 与通配符完全对应。如果你在 Java 中有 Class&lt;?&gt;,你需要在 Kotlin 中使用 Class&lt;*&gt;,而不是 Class&lt;Any&gt;*

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 中的强制转换的唯一原因是因为您使用的是原始类型(如 Turing85 的评论所述)并且编译器基本上放弃了类型检查。

【讨论】:

  • 非常感谢!你一针见血。如有必要,我会尽力重构代码以避免使用原始类型(并了解如何在 Kotlin 中注意到它们)
  • 你不需要在 Kotlin 中注意到它们,因为 Kotlin 不支持它们。困难的部分是在 Java 中注意到它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
相关资源
最近更新 更多