首页 > 解决方案 > 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 将返回一个水果响应列表,它是基本响应列表的子类型。将非常感谢有关此问题的任何指导或解决此问题的替代方法

标签: androidgenericskotlinpolymorphismretrofit

解决方案


我被这个编译器错误难住了,这表明FruitResponseList它不是参数化基类 ( BaseResponseList<FruitAttributes>) 的子类型:

它是 的子类型BaseResponseList<FruitAttributes>,而不是 的子类型BaseResponseList<BaseAttributes>

我尝试在 BaseAttributes 中使用声明点协方差来告诉编译器我的意图,即 FruitResponseList 是基本响应列表的子类,如下所示:...

这可能是一个正确的方法,但问题是它Item不是协变的(它不可能是因为attributesis 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
}

推荐阅读