android - Kotlin 中的类继承和参数化类型:泛型可以实现子类型多态吗?
问题描述
我正在努力理解和/或让 Kotlin 泛型和多态性为我工作。考虑这段代码:
class Item<T: BaseAttributes> {
var id: Long = -1L
lateinit var type: String
lateinit var attributes: T
}
open class BaseAttributes {
lateinit var createdAt: String
lateinit var updatedAt: String
}
open class BaseResponseList<T : BaseAttributes> {
lateinit var items: List<Item<T>> // the collection of items fetched from an API
}
class FruitAttributes(val id: Long, val color: String /* ... */) : BaseAttributes()
class FruitResponseList: BaseResponseList<FruitAttributes>()
// base service for all types of items
interface ApiService {
fun getItems(): BaseResponseList<BaseAttributes>
// fun getItemById(itemId: Long): BaseResponse<BaseAttributes>
/* other CRUD functions here ... */
}
// service for fruits
interface FruitService: ApiService {
override fun getItems(): FruitResponseList // get fruit items
}
我被这个编译器错误难住了,这表明FruitResponseList
它不是参数化基类 ( BaseResponseList<FruitAttributes>
) 的子类型:
Return type of 'getItems' is not a subtype of the return type of the overridden member 'public abstract fun getItems(): BaseResponseList<BaseAttributes> defined in ApiService'
我尝试在 BaseAttributes 中使用声明点协方差来告诉编译器我的意图,即 FruitResponseList 是基本响应列表的子类,如下所示:
open class BaseResponseList<out T : BaseAttributes> {
lateinit var items: List<Item<T>> // the collection of items fetched from an API
}
导致此错误:
Type parameter T is declared as 'out' but occurs in 'invariant' position in type List<Item<T>>
如何实现 Fruit & Base 响应列表之间的类型-子类型关系?
上下文
我正在实现网络代码以针对基于JSON API 规范格式的 API 执行 CRUD 操作,因此我创建了属性和数据 ( Item
) 类来表示 json 响应对象。
我的目标是减少重复代码的数量,这样我只需为应用程序中的每个实体(水果、供应商、买家等)编写一次 API 服务声明。我还想避免为我的应用程序中的每个实体(在干净架构的上下文中)编写数据存储库层的重复/样板实现。我应该能够只指定特定于业务实体的类型(模型/实体),并让一个通用实现来完成获取网络数据的工作。
我认为使用泛型和继承来实现这一点是有意义的。在这个特定示例中,想法是特定于水果的 GET 将返回一个水果响应列表,它是基本响应列表的子类型。将非常感谢有关此问题的任何指导或解决此问题的替代方法
解决方案
我被这个编译器错误难住了,这表明
FruitResponseList
它不是参数化基类 (BaseResponseList<FruitAttributes>
) 的子类型:
它是 的子类型BaseResponseList<FruitAttributes>
,而不是 的子类型BaseResponseList<BaseAttributes>
。
我尝试在 BaseAttributes 中使用声明点协方差来告诉编译器我的意图,即 FruitResponseList 是基本响应列表的子类,如下所示:...
这可能是一个正确的方法,但问题是它Item
不是协变的(它不可能是因为attributes
is avar
并且它的 setter 需要一个T
参数)。如果Item
可以修改以避免这种情况,很好。
另一种方法是将类型参数添加到ApiService
:
// base service for all types of items
interface ApiService<T: BaseAttributes> {
fun getItems(): BaseResponseList<T>
// fun getItemById(itemId: Long): BaseResponse<T>
/* other CRUD functions here ... */
}
// service for fruits
interface FruitService: ApiService<FruitAttributes> {
override fun getItems(): FruitResponseList // get fruit items
}
推荐阅读
- pointers - 如何取消引用 Inno Setup Pascal 脚本中的指针?
- vue.js - Vue JS Jest:mount() 无法正确渲染
- reactjs - 反应:如何观察我的 UseRef 变量的属性变化(UseEffect 不起作用)
- javascript - 我无法理解为什么我无法对我在 React 程序中设置的状态返回的数据进行 .map 映射
- excel - 子或函数未定义excel vba错误
- python - 是否可以将选择字段选择映射到 django 中的外键
- r - 在同一图中绘制正态分布和二项式分布
- c - Win api上ListView中的列过滤器
- reactjs - 如果服务器返回错误,如何取消选中开关?
- mysql - 如何删除名称为`的表