我有一个抽象类,我们称之为 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
}
...
它有一个抽象映射器。重点是我可以将不同的对象映射到实体并使用 read
和write
.
我的子类,我们称之为 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/