Amr ElAdawy Amr ElAdawy - 1 year ago 37
Java Question

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.

Answer Source

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
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download