Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call super class constructor in Kotlin, Super is not an expression

Tags:

kotlin

I have two classes Entity and Account as

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)
    }
}

Which gives me the error

Super is not an expression, it can be only used in the left-hand side of a dot '.'

Why cannot I do that?

The following will pass the compilation error, but I am not sure if it is correct.

 constructor(entity: Entity) : this() {
    super.fromEntity(entity)
}
like image 224
Amr ElAdawy Avatar asked Jun 11 '17 07:06

Amr ElAdawy


People also ask

How do you call a super class constructor in Kotlin?

If the subclass does not contain primary constructor, then we have to call the superclass constructor (primary or secondary) from the secondary constructor of subclass using the super keyword. We also need to initialize the superclass secondary constructor using the parameters of subclass.

Can we call super class constructor?

Use of super() to access superclass constructor As we know, when an object of a class is created, its default constructor is automatically called. To explicitly call the superclass constructor from the subclass constructor, we use super() . It's a special form of the super keyword.

What does calling the super () method do?

Definition and Usage It is used to call superclass methods, and to access the superclass constructor. The most common use of the super keyword is to eliminate the confusion between superclasses and subclasses that have methods with the same name.


Video Answer


3 Answers

You have a couple of problems in your code.

First, this is the correct syntax, to call a super constructor from a secondary constructor:

constructor(entity: Entity) : super(entity)

Second, you can't call a super constructor from a secondary constructor if your class has a primary constructor (which your class does).

Solution 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)
}

Here, the copy constructor is in the child class which just delegates to the primary constructor.

Solution 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
    }
}

Here I'm only using secondary constructors in the child class which lets me delegate them to individual super constructors. Notice how the code is pretty long.

Solution 3 (most idiomatic)

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()

Here I omitted the copy constructors and made the properties abstract so the child class has all the properties. I also made the child class a data class. If you need to clone the class, you can simply call account.copy().

like image 115
Kirill Rakhman Avatar answered Oct 18 '22 20:10

Kirill Rakhman


You can also move your primary constructor down into the class like this:

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

Advantage of this is, compiler will not require your secondary constructor to call primary constructor.

like image 42
Jeff Avatar answered Oct 18 '22 20:10

Jeff


Another option is to create companion object and provide factory method e.g.

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)
        }
    }
}
like image 43
ZZ 5 Avatar answered Oct 18 '22 22:10

ZZ 5