Kotlin - 扩展泛型 class 并返回子类型时出现问题
Kotlin - Problem extending a generic class and returning a subtype
我有以下情况:
- 一个抽象泛型class
abstract class K<T> {
abstract fun bind(itemModel: T)
}
- 从
K
延伸的 class
class A : K<ModelA> {
override fun bind(itemModel: ModelA) {
// ... do anything
}
}
class B : K<ModelB> {
override fun bind(itemModel: ModelB) {
// ... do anything
}
}
- 另一个class,这是一个return基于某些参数的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
方法的 return 类型部分遇到错误,它说:"One type argument expected for class K< T >"。很明显,我正在尝试在 create
方法中 return K 的子类型,这是唯一重要的事情,而不是泛型的特定类型。
这在 Java 中是可能的。 Kotlin 的正确方法是什么?
这里有很多地方需要改进(根据评论更新)。
- 如果参数不是
TYPE_A
或TYPE_B
,则需要添加默认值
- 将 * 添加到参数化 K return 类型。
结果会是这样的:
fun create(type: Int): K<*> {
return when (type) {
TYPE_A -> A()
TYPE_B -> B()
else -> throw IllegalStateException("useful message")
}
}
问题是 K
不是完整类型,就像你不能 return 一个 Java 中的 List
- 你必须 return一个List<Something>
。所以你需要说这将 return a K<Any>
(或其他一些类型绑定)。
但是,现在您还有类型差异问题,因为 K<A>
不是 K<Any>
的子类型,除非 K
的声明是 K<out T>
但您的 bind
方法在 in
位置有 T
,所以这是不可能的。这是有道理的,因为如果我给你一个调用 create
的结果,你无法知道你可以传递什么类型的东西给 bind
所以它没有那么有用。
如之前的回复所述,您可以创建 return K<*>
并且可以编译,但我不确定这会有多大用处,因为您必须知道实际类型为了调用 bind
- 那就是你必须转换任何 create
returns 在这种情况下你可能只构建特定类型的东西开始。
您可以通过返回 K<*>
使其编译,但您无法对返回的实例做很多事情。在 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>
}
我有以下情况:
- 一个抽象泛型class
abstract class K<T> {
abstract fun bind(itemModel: T)
}
- 从
K
延伸的 class
class A : K<ModelA> {
override fun bind(itemModel: ModelA) {
// ... do anything
}
}
class B : K<ModelB> {
override fun bind(itemModel: ModelB) {
// ... do anything
}
}
- 另一个class,这是一个return基于某些参数的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
方法的 return 类型部分遇到错误,它说:"One type argument expected for class K< T >"。很明显,我正在尝试在 create
方法中 return K 的子类型,这是唯一重要的事情,而不是泛型的特定类型。
这在 Java 中是可能的。 Kotlin 的正确方法是什么?
这里有很多地方需要改进(根据评论更新)。
- 如果参数不是
TYPE_A
或TYPE_B
,则需要添加默认值
- 将 * 添加到参数化 K return 类型。
结果会是这样的:
fun create(type: Int): K<*> {
return when (type) {
TYPE_A -> A()
TYPE_B -> B()
else -> throw IllegalStateException("useful message")
}
}
问题是 K
不是完整类型,就像你不能 return 一个 Java 中的 List
- 你必须 return一个List<Something>
。所以你需要说这将 return a K<Any>
(或其他一些类型绑定)。
但是,现在您还有类型差异问题,因为 K<A>
不是 K<Any>
的子类型,除非 K
的声明是 K<out T>
但您的 bind
方法在 in
位置有 T
,所以这是不可能的。这是有道理的,因为如果我给你一个调用 create
的结果,你无法知道你可以传递什么类型的东西给 bind
所以它没有那么有用。
如之前的回复所述,您可以创建 return K<*>
并且可以编译,但我不确定这会有多大用处,因为您必须知道实际类型为了调用 bind
- 那就是你必须转换任何 create
returns 在这种情况下你可能只构建特定类型的东西开始。
您可以通过返回 K<*>
使其编译,但您无法对返回的实例做很多事情。在 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>
}