【问题标题】:Kotlin - Problem extending a generic class and returning a subtypeKotlin - 扩展泛型类并返回子类型的问题
【发布时间】:2020-06-10 21:13:11
【问题描述】:

我有以下情况:

  • 抽象的泛型类
abstract class K<T> {
  abstract fun bind(itemModel: T)
}
  • K 扩展的类
class A : K<ModelA> {
    override fun bind(itemModel: ModelA) {
        // ... do anything
    }
}

class B : K<ModelB> {
    override fun bind(itemModel: ModelB) {
        // ... do anything
    }
}
  • 另一个类,这是一个根据某些参数返回 K 子类型的工厂:
class Factory {
    companion object {
        const val TYPE_A: Int = 0
        const val TYPE_B: Int = 1

        fun create(type: Int): K {
            return when (type) {
                TYPE_A -> A()
                TYPE_B -> B()
            }
        }
    }
}

此时,Factory.create 方法的返回类型部分出现错误,它显示:“类 K 需要一个类型参数”。很明显,我试图在 create 方法中返回 K 的子类型,这是唯一重要的事情,而不是泛型的特定类型。

这在 Java 中是可能的。 Kotlin 中的正确方法是什么?

【问题讨论】:

    标签: java kotlin generics hierarchy


    【解决方案1】:

    这里有很多地方需要改进(根据评论更新)。

    • 如果参数不是TYPE_ATYPE_B,则需要添加默认值
    • 将 * 添加到参数化的 K 返回类型。

    结果会是这样的:

    fun create(type: Int): K<*> {
       return when (type) {
           TYPE_A -> A()
           TYPE_B -> B()
           else -> throw IllegalStateException("useful message")
        }
    }
    

    【讨论】:

      【解决方案2】:

      问题是K 不是一个完整的类型,就像你不能在Java 中返回List - 你必须返回一个List&lt;Something&gt;。所以你需要说这将返回一个K&lt;Any&gt;(或其他类型的绑定)。

      但是,现在您还有一个类型差异问题,因为 K&lt;A&gt; 不是 K&lt;Any&gt; 的子类型,除非 K 的声明是 K&lt;out T&gt; 但您的 bind 方法在 @ 中有 T 987654331@ 位置,所以这是不可能的。这是有道理的,因为如果我给你一个调用 create 的结果,你就无法知道你可以将什么类型的东西传递给 bind,所以它没有那么有用。

      如上一个回复中所述,您可以创建 return K&lt;*&gt; 并且可以编译,但我不确定这会有多大用处,因为您必须知道实际类型才能调用 bind -也就是说,您必须转换 create 返回的任何内容,在这种情况下,您可能只需构造特定类型的东西即可。

      【讨论】:

        【解决方案3】:

        您可以通过返回K&lt;*&gt; 使其编译,但您无法对返回的实例做太多事情。在 Java 中,您可以通过忽略编译器警告来处理原始类型,并在开始尝试对返回的对象调用方法时冒着 ClassCastExceptions 的风险,但是为什么还要使用泛型呢?

        相反,您可以使用具体类型,以便调用者可以传递生成的实例可以处理的类型,而不是某个数字:

            @Suppress("UNCHECKED_CAST")
            inline fun <reified T> create(): K<T> {
                return when (T::class) {
                    ModelA::class -> A()
                    ModelB::class -> B()
                    else -> error("unsupported type")
                } as K<T>
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-08
          • 1970-01-01
          • 2021-03-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多