【问题标题】:Class inheritance and parametrized types in Kotlin: Is Subtype Polymorphism possible with generics?Kotlin 中的类继承和参数化类型:泛型可以实现子类型多态吗?
【发布时间】:2018-10-24 14:27:12
【问题描述】:

我很难理解和/或让 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&lt;FruitAttributes&gt;) 的子类型:

Return type of 'getItems' is not a subtype of the return type of the overridden member 'public abstract fun getItems(): BaseResponseList&lt;BaseAttributes&gt; 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&lt;Item&lt;T&gt;&gt;

如何实现 Fruit 和 Base 响应列表之间的类型-子类型关系?

背景

我正在实现网络代码以针对基于JSON API spec 格式的 API 执行 CRUD 操作,因此我创建了属性和数据 (Item) 类来表示 json 响应对象。

我的目标是减少重复代码的数量,这样我只需为我的应用程序中的每个实体(水果、供应商、买家等)编写一次 API 服务声明。我还想避免为我的应用程序中的每个实体(在干净架构的上下文中)编写数据存储库层的重复/样板实现。我应该能够只指定特定于业务实体的类型(模型/实体),并让一个通用实现来完成获取网络数据的工作。

我认为使用泛型和继承来实现这一点是有意义的。在这个特定示例中,想法是特定于水果的 GET 将返回一个水果响应列表,它是基本响应列表的子类型。非常感谢任何有关此问题的指导或解决此问题的替代方法

【问题讨论】:

标签: android generics kotlin polymorphism retrofit


【解决方案1】:

为什么不这样:

// base service for all types of items
interface ApiService<T> {
    fun getItems(): T
    // fun getItemById(itemId: Long): BaseResponse<BaseAttributes>
    /* other CRUD functions here ... */
}

// service for fruits
interface FruitService: ApiService<FruitResponseList> {

    override fun getItems(): FruitResponseList
}

或者这个:

// base service for all types of items
interface ApiService<T : BaseAttributes> {
    fun getItems() : BaseResponseList<T>
    // fun getItemById(itemId: Long): BaseResponse<BaseAttributes>
    /* other CRUD functions here ... */
}

// service for fruits
interface FruitService: ApiService<FruitAttributes> {

    override fun getItems(): FruitResponseList
}

在您当前的实现中,您将被绑定到BaseResponseList&lt;BaseAttributes&gt;

【讨论】:

    【解决方案2】:

    我被这个编译器错误难住了,它暗示 FruitResponseList 不是参数化基类 (BaseResponseList&lt;FruitAttributes&gt;) 的子类型:

    它是BaseResponseList&lt;FruitAttributes&gt; 的子类型,它不是BaseResponseList&lt;BaseAttributes&gt; 的子类型。

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

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

    【讨论】:

      【解决方案3】:

      Mark 和 Alexey 提供的答案非常有帮助。特别是,在BaseAttributes 上参数化ApiService 是更灵活的选择,因为它允许函数接受或返回BaseResponseListBaseResponse [sub] 类型。

      然而,不管怎样,事实证明,Retrofit 库不允许其服务声明扩展其他接口,甚至不允许参数化,据说有利于组合而不是继承。关于他们对此issue here 的决定有很多争论。 所以我最终为我的每个模型创建了单独的接口:/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-02
        • 1970-01-01
        • 1970-01-01
        • 2018-07-14
        • 2011-08-13
        • 1970-01-01
        • 2012-11-29
        • 2015-11-18
        相关资源
        最近更新 更多