使用类型约束对通用 Kotlin 类进行子类化

问题描述 投票:0回答:1

我在 Kotlin 中组合子类化多态性和泛型(临时多态性)时遇到了问题。

这是我的类型定义:

interface State

interface StatefulContainer<out S : State> {
    val state: S
}

interface StatefulContainerRepository<C: StatefulContainer<S>, S: State>

// Specialization starts here
sealed interface MyState : State

enum class StateA : MyState { A1, A2 }
enum class StateB : MyState { B1, B2 }

sealed interface MyEntity : StatefulContainer<MyState> {
    override val state: MyState
}

data class EntityA(override val state: StateA) : MyEntity
data class EntityB(override val state: StateB) : MyEntity

sealed interface MyContainerRepository<C: StatefulContainer<S>, S: MyState>: StatefulContainerRepository<C, S>

class ARepository: MyContainerRepository<EntityA, StateA>

类型检查器返回以下错误:

Type argument is not within its bounds. Expected: StatefulContainer<StateA>. Found: EntityA
。这很奇怪,因为
EntityA
是一个
StatefulContainer<StateA>
——至少我是这么认为的。但是,如果我进行以下修改:

data class EntityA(override val state: StateA) : MyEntity, StatefulContainer<StateA>

类型检查器抱怨

Type parameter S of 'StatefulContainer' has inconsistent values: MyState, StateA
。为什么会这样呢?如何正确输入上面的类层次结构?

我习惯了 Haskell 等语言的更直接的参数多态性,或者纯 OO 子类型。这里的组合,再加上 JVM 的类型擦除,让我很难理解发生了什么。因此,除了上面的具体问题之外,我还希望获得一些更基本的见解 - 哪些概念在起作用,如果我设法理解它们,将帮助我解决这个问题和类似的情况?

kotlin generics types polymorphism subtyping
1个回答
0
投票

这很奇怪,因为

EntityA
StatefulContainer<StateA>

这不是真的。

EntityA
是一个
MyEntity
,而那只是一个
StatefulContainer<MyState>
,你希望它比实际情况更具体。只有以下内容才有效:

class ARepository: MyContainerRepository<EntityA, MyState>

或者,您可以通过另一种方式声明

EntityA
。你建议这个:

data class EntityA(override val state: StateA) : MyEntity, StatefulContainer<StateA>

但这也不起作用,因为

StatefulContainer<StateA>
MyEntity
不兼容。他们的属性不匹配。让我们看看如果上述方法可行会发生什么。为了更好地演示它,我们首先让
MyEntity
将其属性覆盖为
var
而不是
val
(*):

override var state: MyState

然后考虑这段代码:

val entityA: StatefulContainer<StateA> = EntityA(StateA.A1)
val myEntity: MyEntity = entityA
myEntity.state = StateB.B1

首先,我们创建 EntityA 的一个新实例,然后将其转换为

MyEntity
。通过上面的
EntityA
声明,应该可以正常工作,因为它具有
MyEntity
StatefulContainer<StateA>
两种类型。前两行很好。然后让我们看最后一行:乍一看一切似乎都正常:我们更改
state
MyEntity
值并将其设置为允许的子类型之一。但请记住,底层对象实际上是
EntityA
的实例。并且 都不允许其值被更改(它被声明为
val
),nor 也不能保存
StateB.B1
(因为它是
StateA
类型)。这里出了严重的问题。上面的代码没有错误,所以它一定是
EntityA
的声明:它显然不可能同时是
MyEntity
a
StatefulContainer<StateA>

只需删除

MyEntity
作为
EntityA
的子类型,如下所示:

data class EntityA(override val state: StateA) : StatefulContainer<StateA>

现在您的初始代码将按预期工作。

(*):这是完全合法的,因为通过覆盖它会创建一个新属性,该属性可以具有任何特征,只要它具有also来自超类的特征。

© www.soinside.com 2019 - 2024. All rights reserved.