Kotlin: Delegate to multiple interfaces with same implementation

Viewed 717

Situation/Problem

I would like to extend a library via kotlin with nice delegation features, but I got stuck at a place where I want to delegate two different interfaces to the same impl. The problem is, that these two interfaces have common methods (one extends the other).

Library I cannot change

So there is a Base interface that is extended by both ExtendedBase and Foo1 interfaces. And a Foo1Impl that implements Foo and ExtendedBase, actually there are a lot of implementations and interfaces like that in the library.

interface Base: {
  fun doBase()
  fun doBase2()
  // .. lots of methods
  fun doBaseN()
}

interface ExtendedBase: Base {
  fun doExtended()
  fun doExtended2()
  // .. lots of methods
  fun doExtendedN()
}

interface Foo1: Base {
  fun doFoo1()
}

class Foo1Impl: Foo1, ExtendedBase {
  // some impl for doBase, doExtended and doFoo1
}

interface Foo2: Base {
  fun doFoo2()
}

class Foo2Impl: Foo2, ExtendedBase {
  // ...
}

// ... lots of interfaces and impls

interface FooN: Base {
  fun doFooN()
}

class FooNImpl: FooN, ExtendedBase {
  // ...
}

Extension of that library (This is what I would like to Do)

I want to extend this library and create my own interfaces that extend the functionality of something that is ExtendedBase and Foo[1..N]:


interface FooBar1: Foo1 {
  fun doFooBar1()
}

class FooBar1Impl<T>
    constructor(impl: T): FooBar1, ExtendedBase by impl, Foo1 by impl
        where T: Foo1, T: ExtendedBase
{

    override fun doFooBar1() {
        println("foobar1")
    }
}

//.. a lot of other extensions like FooBar[2..N] that all extended the bases with the same logic

These extensions would complain about inheriting multiple implementations of it because both Foo1 and ExtendedBase extend Base. But actually it would be easily decidable because there is only one implementation to delegate to: impl: T.

Things I thought about which do/did not work

1. Syntax for multi-interface-implementation (not existing in kotlin)

If there would be a syntax for multi-interface implementations, it might be possible like so:

class FooBar1Impl<T>
    constructor(impl: T): FooBar1, (ExtendedBase, Foo1) by impl
        where T: Foo1, T: ExtendedBase
{ /* ... */ }

2. Inheritance of typeparameter declarations (not possible for jvm)

If it would be possible for the jvm one could write something like this, but it is not:

class FooBar1Impl<T>
    constructor(impl: T): FooBar1, T by impl
        where T: Foo1, T: ExtendedBase
{ /* ... */ }

3. Nearly possible: base-object that can be extended (still problematic)

open class BaseExtension constructor(impl: Base) : Base by impl

@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class FooBar1Impl<T>
    constructor(impl: T): BaseExtension(impl), FooBar1
        where T: Base, T: ExtendedBase

But here you can see the suppressed warning is the problem.

4. Syntax for deciding the default impl (not existing in kotlin)

class FooBar1Impl<T>
    constructor(impl: T): FooBar1, [default] ExtendedBase by impl, Foo1 by impl
        where T: Foo1, T: ExtendedBase { /* .. */ }

Question

Is there any workflow that saves me from overriding all Base methods by hand? Or any other solution that makes this kind of extension even possible?

Imagine Base has like 100 methods that I would need to manually override.

1 Answers

Ok I think I might have figured out a solution for now:

At first I will create a base extension class structure, which delegates methods from both: Base and BaseExtension to the same constructor-property. I need/want to suppress a warning for this...

The warning is there because kotlin cannot see that the superclass methods are all delegated to the same implementation impl that this implementation delegates the overriding methods to :

open class BaseExtension<in T: Base>
    constructor(private val impl: T) : Base by impl

@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class Extension<in T>
    constructor(private val impl: T) : BaseExtension<T>(impl), ExtendedBase by impl
        where T: Base, T: ExtendedBase {
}

Then for every explicit extension I will subclass this Extension and implement the explicitly extended interfaces like so:

interface FooBar1: Foo1 {
    fun doFooBar1()
}

@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class FooBar1Impl<in T>
constructor(private val impl: T) : FooBar1, Extension<T>(impl), Foo1 by impl
    where T: Base, T: ExtendedBase, T: Foo1
{
    fun doFooBar() { /* .. */ }
}

In the end this is not a very concise solution, but at least there are no errors and I do not have to implement any already implemented method again. The warning though still bugs me, but I cannot see any way around that.


If there is anybody with a solution that does not include these warnings that need to be suppressed, it would be great.

Related