Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple inheritance with one class and interfaces in Kotlin

Tags:

kotlin

My intended goal is to extend multiple classes from an existing library by a fixed set of attributes and methods (whose names will not overlap).

The straight forward solution would be simple inheritance: derive the library class and add the attributes/methods. But it would be quite repetitive as the attributes and methods will be the same for all derived classes. It would be better to create an auxilliary class with all attributes and methods I need and simply derive the extended classes from both the library class and the auxilliary class.

However, as it seems there is no multiple inheritance in Kotlin comparable to C++ , I wanted to use interfaces to make it work, where the interface would hold all the attributes and methods I need.

I have started with the following simplistic code to test interfaces:

open class LibraryClass{
    var x: Int = 0

    fun setMyX(x_: Int){
        x = x_
    }
}

interface MyInterface{
    var y: Int
    var z: Int
    var abc: Int

    fun myMethod(y_: Int){
        y = y_
        z = y*y
    }
} 

class InheritanceTest: LibraryClass(), MyInterface{    
    fun print(){
    println("My values: $x, $y, $z")
    }
}

fun main(){
    var test = InheritanceTest()
    test.setMyX(1)
    test.myMethod(5)
    test.print()    
}

If I try to compile this code, I will get the following error message:

error: class 'InheritanceTest' is not abstract and does not implement abstract member public abstract var y: Int defined in MyInterface

Some research showed that I need to override the interface's attributes in the primary constructor of the derived class:

class InheritanceTest(override var y: Int, override var z: Int, override var abc: Int)

and then

var test = InheritanceTest(2,3,0)

Overriding all(?) interface attributes (even unused ones) in the derived class seems quite repetitive (and it's not possible to initialize attributes in the interface either), which basically beats the purpose of why I originally intended to use interfaces: to save repetition. (Except for methods, which seem not to have to be overridden as long as one class and (multiple) interfaces do not share methods of the same name).

My interposed question is: Is this the only way to do it or solve the error?

Then I found something else to try out in the interface:

var fgh: Int 
    get()  { return fgh }
    set(v) { fgh = v }

No overriding necessary so far. But I could not yet get how to actually use the attribute fgh in either a method or by setting a value.

When I try to set fgh by test.fgh = 100 in main(), I get the following error:

Exception in thread "main" java.lang.StackOverflowError
    at MyInterface$DefaultImpls.setFgh(

Then trying to use test.setFgh(100) (since the previous error message implies the method exists) yields:

error: unresolved reference: setFgh

Trying to define setFgh(v: Int) {fgh = v} results in:

error: platform declaration clash: The following declarations have the same JVM signature (setFgh(I)V):
    fun <set-fgh>(v: Int): Unit defined in MyInterface
    fun setFgh(v: Int): Unit defined in MyInterface

Alright, defining a differently named method similar to myMethod():

fun myOtherMethod(v: Int){    
    fgh = v 
    z = fgh * fgh
}

And then using myOtherMethod(10) in main() gives again:

Exception in thread "main" java.lang.StackOverflowError
    at MyInterface$DefaultImpls.setFgh

So, I'm now really lost as to what do in order to get a non-repetitive working solution for extending multiple classes with the same attributes and methods.

Edit: I now understand that interfaces aren't exactly what I was going for, as one has to implement (not override, which took me a while to get as it is done by the keyword "override") everything in the derived classes.

like image 822
mousou Avatar asked Mar 03 '23 12:03

mousou


1 Answers

You were on the right track with your first try. You really have to override the attributes in the interface, but it doesn't have to be done in the constructor. You can do it in the class body and that way you can add a default value for them (not only can, but you must). If you make that class open you can then extend from it in all your classes.

open class InheritanceTest: LibraryClass(), MyInterface {
    override var y: Int = 0
    override var z: Int = 0
    override var abc: Int = 0

    fun print() {
        println("My values: $x, $y, $z")
    }
}

With this class your original main will print My values: 1, 5, 25, and you will be able to inherit from this class without having to override/initialize anything (but you can use the init block to customize your superclass). For example:

class MyClass : InheritanceTest() {
    init {
        myMethod(4)
    }
}

fun main() {
    val test = MyClass()
    test.setMyX(2)
    test.print()
}

will print My values: 2, 4, 16

I hope I understood your question correctly and could help :)

like image 191
pshegger Avatar answered May 23 '23 19:05

pshegger