gpt4 book ai didi

java - Kotlin 继承与泛型

转载 作者:行者123 更新时间:2023-12-02 13:49:22 24 4
gpt4 key购买 nike

我有一个抽象类,我们称之为 A。

abstract class A(private val name: String) {

fun read(key: String): Entity {
...
}

fun write(entity: Entity) {
...
}

abstract val mapper: Mapper<Any>

...

interface Mapper<T> {
fun toEntity(entry: T): Entity

fun fromEntity(entity: Entity): T
}

...

它有一个抽象映射器。重点是我可以将不同的对象映射到实体并使用 readwrite .

我的子类,我们称之为 B,结构如下:

class B(private val name: String) : A(name) {
override val mapper = AnimalMapper

object AnimalMapper: Mapper<Animal> {
override fun fromEntity(entity: Entity): Animal {
TODO("not implemented")
}

override fun toEntity(animal: Animal): Entity {
TODO("not implemented")
}
}
}

理想情况下,我希望在Mapper中有一个界面通用而不是 Any ,但我只是为了这个问题而简化它。

问题是我收到此错误:

Type of 'mapper' is not a subtype of the overridden property 'public abstract val mapper: Mapper<Any> defined in ...

这是为什么?

最佳答案

请注意有关继承和泛型的两个事实:

  • A val属性只能用原始属性类型的子类型覆盖。这是因为该类型的所有用户都希望它返回原始类型的某个实例。例如,可以覆盖 CharSequence属性 String .

    A var属性甚至不能使用子类型,只能使用原始类型,因为用户可能希望将原始类型的实例分配给属性。

  • Kotlin 泛型 are invariant by default 。鉴于Mapper<T> ,其任何两个实例化 Mapper<A>Mapper<B>如果 A 则不是彼此的子类型和B是不同的。

鉴于此,您无法覆盖 Mapper<Any> 类型的属性与 Mapper<SomeType> ,因为后者不是前者的子类型。

您不能使用declaration-site variance使所有Mapper<T>用法协变(将接口(interface)声明为 interface Mapper<out T> ),因为 T用作 fun toEntity(entry: T): Entity 中的参数类型.

您可以尝试申请use-site variance ,将属性声明为

abstract val mapper: Mapper<out Any>

但是这样类的用户A无法调用fun toEntity(entry: T): Entity ,因为他们不知道替换 Any 的实际类型是什么。在子类中,因此他们可以安全地通过 entry 。但是,如果用户知道确切的类型(例如 B ),他们将看到 mapper 的类型。正如它在被重写的属性中声明的那样。

<小时/>

可以让您以更灵活的方式使用重写属性的常见模式是参数化父类 class A<T>并将属性定义为 val mapper: Mapper<T> .

这样,子类型就必须指定哪个 T他们在声明中使用:class B(...) : A<Animal>(...) ,以及看到 A<Animal> 的用户(即使不知道它实际上是 B<Animal> 也会安全地得到它的 mapper 作为 Mapper<Animal>

关于java - Kotlin 继承与泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46305481/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com