Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting class with primary constructor

Tags:

java

kotlin

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.

like image 358
Amr ElAdawy Avatar asked May 23 '17 08:05

Amr ElAdawy


1 Answers

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
}
like image 147
zsmb13 Avatar answered Oct 25 '22 06:10

zsmb13