Kotlin中如何调用父类构造函数,Super不是一个表达式

89

我有两个类 EntityAccount,定义如下:

abstract class Entity(
    var id: String? = null,
    var created: Date? = Date()) {

    constructor(entity: Entity?) : this() {
        fromEntity(entity)
    }

    fun fromEntity(entity: Entity?): Entity {
        id = entity?.id
        created = entity?.created
        return this;
    }
}

and
data class Account( 
    var name: String? = null,
    var accountFlags: Int? = null
) : Entity() {

    constructor(entity: Entity) : this() {
        super(entity)
    }
}

为什么我不能这样做呢?
以下内容可以通过编译,但我不确定它是否正确。

 constructor(entity: Entity) : this() {
    super.fromEntity(entity)
}

3
你可以在这里找到如何调用父类构造函数的规则:https://kotlinlang.org/docs/reference/classes.html#inheritance。简而言之,如果超类具有主构造函数,则必须从基类的所有构造函数中调用该构造函数。 - zsmb13
constructor(entity: Entity) : super(entity) - Miha_x64
@zsmb13,你能分享一些代码吗? - Amr Eladawy
@Miha_x64 我已经尝试过了,它会提示“需要主构造函数调用”。 - Amr Eladawy
5个回答

123

你的代码有几个问题。

首先,这是从次要构造函数调用超级构造函数的正确语法:

constructor(entity: Entity) : super(entity)

其次,如果你的类有一个主构造函数(而你的类确实有),则无法从次要构造函数中调用超级构造函数。

解决方案1

abstract class Entity(
        var id: String,
        var created: Date
)

class Account(
        var name: String,
        var accountFlags: Int,
        id: String,
        created: Date
) : Entity(id, created) {
    constructor(account: Account) : this(account.name, account.accountFlags, account.id, account.created)
}

这里,复制构造函数在子类中,只是委托给主构造函数。

解决方案2

abstract class Entity(
        var id: String,
        var created: Date
) {
    constructor(entity: Entity) : this(entity.id, entity.created)
}

class Account : Entity {
    var name: String
    var accountFlags: Int

    constructor(name: String, accountFlags: Int, id: String, created: Date) : super(id, created) {
        this.name = name
        this.accountFlags = accountFlags
    }

    constructor(account: Account) : super(account) {
        this.name = account.name
        this.accountFlags = account.accountFlags
    }
}

在子类中,我只使用了次要构造函数,这使我能够将它们委托给单个超级构造函数。请注意代码非常长。

解决方案3(最惯用)

abstract class Entity {
    abstract var id: String
    abstract var created: Date
}

data class Account(
        var name: String,
        var accountFlags: Int,
        override var id: String,
        override var created: Date
) : Entity()

在这里,我省略了复制构造函数并将属性设为抽象,这样子类就拥有了所有的属性。我还将子类设置为data class。如果您需要克隆该类,只需调用account.copy()即可。


2
谢谢。我已经尝试了语法constructor(entity: Entity) : super(entity),但是它给出了相同的错误super is not an expression...。我没有尝试过你建议的代码,但我有一个疑虑。如果我必须在每个子类中重新定义相同的字段,那么拥有父类的好处是什么? - Amr Eladawy
1
就像我在我的答案中所说,“如果你的类有一个主构造函数,你不能从次要构造函数调用超级构造函数”。关于您的担忧,子类没有相同的字段。它具有构造函数参数,将其传递给定义属性的超类。 - Kirill Rakhman
1
如果我从子类中移除了主构造函数,我能调用父级构造函数吗?你能否更新你的答案并提供相关示例代码? - Amr Eladawy
@AmrElAdawy 我在解决方案3中忘记将子类设置为“数据类”。请查看更新后的答案。 - Kirill Rakhman
惯用方法的唯一缺点是你会暴露字段 idcreated,这样就允许任何人传入一个随机的 id 和虚假的创建日期。 - Sync
显示剩余2条评论

16
您也可以将主构造函数移到类中,像这样:

data class Account: Entity {
    constructor(): super()
    constructor(var name: String? = null, var accountFlags: Int? = null): super()
    constructor(entity: Entity) : super(entity)
}

这样做的优点是,编译器不需要你的次要构造函数调用主要构造函数。


1
最简单的解决方案 - Garfield

3
另一种选择是创建伴生对象并提供工厂方法,例如:
class Account constructor(
        var name: String? = null,
        var accountFlags: Int? = null,
        id: String?,
        created: Date?
) : Entity(id, created) {

    companion object {
        fun fromEntity(entity: Entity): Account {
            return Account(null, null, entity.id, entity.created)
        }
    }
}

0

使用 super<Entity>.fromEntity(entity) 调用超类方法。

如文档所述:

在 Kotlin 中,实现继承受以下规则的约束:如果一个类从其直接超类继承了同一成员的多个实现,则它必须重写此成员并提供自己的实现(可能使用其中一个继承的实现)。为了表示从中继承实现的超类型,我们使用带有尖括号的超类型名称限定的 super,例如 super。

constructor(entity: Entity) : this() {
    super<Entity>.fromEntity(entity)
}

想要了解更多,请阅读覆盖规则


谢谢回复。我知道如何表示超类型。但这只是一个解决方法。我想使用超级构造函数。 - Amr Eladawy

0

这正是我在寻找的,没有修饰符或val/var参数,可以用于父级(super)调用

open class Dad(protected val name: String){
}

open class Son(protected val nickname: String, name: String): Dad(name){
...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接