简体   繁体   中英

Inheriting class with primary constructor

I have a parent class as following,

interface ITask { }

open class Task(val targetServer: Server) : ITask { }

Then there a child inheriting it and overriding the primary constructor as following,

data class FileTask(val sourceServer: Server, targetServer: Server) : Task(targetServer = targetServer) {

}

This is throwing a compilation error in eclipse as

Data class primary constructor must have only property (val / var) parameters

Removing the data keyword from the class header will kill the error, but I don't understand why.

Keeping the data keyword and adding var to the targetServer gives another error

'targetServer' hides member of supertype 'Task' and needs 'override' modifier

Adding override to the targetServer to be override var targetServer: Server throws another error

'targetServer' in 'Task' is final and cannot be overridden

I need some help to understand these errors.

The initial error is because a data class can't have parameters in its primary constructor other than val or var properties. Removing the data keyword lifts this restriction.

It's been mentioned that data classes generally don't play well with inheritance. They're supposed to be used as simple data transfer objects, and aren't really suitable for participating in hierarchies, because it becomes hard to understand which properties are going to be considered in the implementations of the generated methods. Your best bet might be to not use them at all here.

For more about data classes and inheritance, here is the proposal that was implemented in Kotlin 1.1.


To get back to the specific problem, if you really have to make this class a data class, you can mark the property in the base class as open and then override it in FileTask , like so:

open class Task(open val targetServer: Server) : ITask

data class FileTask(val sourceServer: Server, override val targetServer: Server): Task(targetServer = targetServer)

This basically hides the property declared in Task , and always accesses the property in FileTask instead.

I don't know what your exact requirements for your classes are, but one thing you could do to clean this up and make it a bit nicer would be to make Task and its targetServer property abstract, like so:

abstract class Task : ITask {
    abstract val targetServer: Server
}

data class FileTask(val sourceServer: Server, override val targetServer: Server) : Task()

This way you wouldn't have the unnecessary property (and backing field) in the base class, and you'd be forced to have a targetServer property in all the classes that inherit from Task . You could also take this a step further, and put the property in the ITask interface as well.

interface ITask {
    val targetServer: Server
}

I know this is a very old post, but I was struggling with the same issue and making my superclass abstract was not a solution. You just need to do the following:

change this

open class Task(val targetServer: Server) : ITask { }

to (Please note, I have made targetServer variable to open)

open class Task(open val targetServer: Server) : ITask { }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM