I have many times case where child inherits all constructor properties from the parent and I was wondering if there is a better way to pass all child argument to parent constructor when inheritance is in place…
Parent class
abstract class DbValue(
val kprop: KProperty1<Any, Any?>,
val dbType: String,
val jdbcType: JDBCType,
val encoder: Encoder<*>,
val decoder: Decoder<*>,
) {
... additional abstract methods...
... with some of them implemented methods...
}
Child class
open class Column(
kprop: KProperty1<Any, Any?>,
dbType: String,
jdbcType: JDBCType,
encoder: Encoder<*>,
decoder: Decoder<*>,
) : DbValue(
kprop = kprop, <------ All properties are passed to parent
dbType = dbType, <------ All properties are passed to parent
jdbcType = jdbcType, <------ All properties are passed to parent
encoder = encoder, <------ All properties are passed to parent
decoder = decoder <------ All properties are passed to parent
) {
... Implemented abstract methods...
... With some additional methods...
}
Personally, I tend to replace abstract classes defining only public properties with interfaces instead. Applied to your example, it gives something like:
interface DbValue {
val kprop: KProperty1<Any, Any?>
val dbType: String
val jdbcType: JDBCType
val encoder: Encoder<*>
val decoder: Decoder<*>
fun doStuff(): Unit {
/* default implementation */
}
fun toBeImplemented(): Unit
// Other methods...
}
open class Column(
override val kprop: KProperty1<Any, Any?>,
override val dbType: String
override val jdbcType: JDBCType
override val encoder: Encoder<*>
override val decoder: Decoder<*>
) : DBValue {
// Implemented abstract methods and other stuff
}
Of course, abstract classes remain useful when you have some protected or private properties/methods, or when you do not want to allow overriding of some functions.
You can apply the same pattern there:
abstract class DbValue {
abstract val kprop: KProperty1<Any, Any?>
abstract val dbType: String
abstract val jdbcType: JDBCType
abstract val encoder: Encoder<*>
abstract val decoder: Decoder<*>
open fun doStuff(): Unit {
/* default implementation */
}
abstract fun toBeImplemented(): Unit
// Other methods...
}
open class Column(
override val kprop: KProperty1<Any, Any?>,
override val dbType: String
override val jdbcType: JDBCType
override val encoder: Encoder<*>
override val decoder: Decoder<*>
) : DBValue() {
// Implemented abstract methods and other stuff
}
I prefer this way to write abstractions, because I tend to find it easier to read, document and maintain. Also, it gives more freedom in implementations to make custom management for a property (overriding getter for example).
Now, if you prefer to put concrete state in abstract classes, I do not know of any better way than your example, at least without additional compiler plugin.
Another option might be to wrap the fields into a new class. You might then have your superclass store a reference to an instance of that class instead of to the individual fields; or you might find that you don’t need your superclass at all and can turn the subclass into a stand-alone class with such a reference. Or you could use it simply as a temporary params holder, and have the superclass take it in its constructor but then unpack it to the existing properties.