tir38
tir38

Reputation: 10421

Kotlin delegate with `by` but create delegate *inside* delegator

I want to use Kotlin delegation but I don't want to create the delgate outside the delegator. All the samples on delegation all look like this:

interface Worker {
    fun doWork()
}

class Supervisor(workerDelegate: Worker) : Worker by workerDelegate {
}

class Delegate : Worker {
    override fun doWork() {
        // actual work
    }
}

fun main() {
    val delegate = Delegate()
    val supervisor = Supervisor(delegate)
    supervisor.doWork() // delegates to delegate
}

But I want to create the Delegate inside the Supervisor. Something like this:

class Supervisor : Worker by workerDelegate {
    init {
        val workerDelegate = Delegate()
    }
}

Is something like this possible?

Upvotes: 3

Views: 418

Answers (3)

comdiv
comdiv

Reputation: 941

You should understand 2 things:

  1. (..., some:IFace) : IFace by some working only with ctor parameter and only during creation (!) not after creation, it's not "lazy" and it not bind "field" or "property" it binds instance that was given at creation. So! It's not "delegation" in expected way it's just hint to compiler to substitute all IFace calls to instance that is already in stack.

  2. while init{...} is a part of construction logic of class it works on members of class, but cannot replace parameters that was already processed from ctor in by operand.

  3. The most advanced way is just to create "static" factory method to provide some logic to evaluate parameter for ctor.

Assume all this things said before here :

package codes.spectrum.serialization_json.excel

import io.kotlintest.shouldBe
import io.kotlintest.specs.StringSpec

class ItIsNotWorking : StringSpec() {

    interface IDo {
        fun doIt(): Int
        companion object {
            val STUB = object :IDo {
                override fun doIt(): Int {
                    error("I am just stub")
                }
            }
        }
    }


    class Do1 : IDo {
        override fun doIt() = 1
    }

    class Do2 : IDo {
        override fun doIt() = 2
    }

    // i try to make it as parameter in ctor but as var, wishing that it will bind it each call,
    // not just during construction
    class SuperDo private constructor(param:Int,  var doiter: IDo):IDo by doiter{
        // imagine that at constructor point you cannot still decide what IDo impl you require
        constructor(param: Int) : this(param, IDo.STUB)
        init {
            // here i try some logic to lately (after construction) to setup doiter
            if(param % 2 == 1){
                doiter = Do1()
            }else{
                doiter = Do2()
            }
        }
    }



    init {
        // that is my expectations

        "When with 1 it will be Do1" {
            SuperDo(1).doiter.doIt() shouldBe 1 // ok!!!
            SuperDo(1).doIt() shouldBe 1 // fail!!!
        }
        "When with 2 it will be Do2" {
            SuperDo(2).doiter.doIt() shouldBe 2 //ok!!!
            SuperDo(2).doIt() shouldBe 2 //fail!!
        }

        // Uffff!!!! It's not working at all (!!!)


    }

    class NotSuperDo private constructor(val doiter: IDo):IDo by doiter{
        // imagine that at constructor point you cannot still decide what IDo impl you require
        constructor(param: Int) : this(buildDoiter(param))

        companion object {
            fun buildDoiter(param: Int) : IDo =
                    if(param % 2 == 1){
                        Do1()
                    }else{
                        Do2()
                    }
        }
    }

    init {
        // that is my expectations

        "not-super When with 1 it will be Do1" {
            NotSuperDo(1).doiter.doIt() shouldBe 1 // ok!!!
            NotSuperDo(1).doIt() shouldBe 1 // ok!!!
        }
        "not-super When with 2 it will be Do2" {
            NotSuperDo(2).doiter.doIt() shouldBe 2 //ok!!!
            NotSuperDo(2).doIt() shouldBe 2 //ok!!
        }

        // It worked! But it still just better constructor - not case to substitute delegated iface


    }

}

Upvotes: 0

Naor Tedgi
Naor Tedgi

Reputation: 5707

just use a private constructor.

class Supervisor private constructor(workerDelegate: Worker) : Worker by workerDelegate {
      constructor() : this(Delegate())

}

fun main() {
    val delegate = Delegate()
    val supervisor = Supervisor()
    supervisor.doWork() // delegates to delegate
}

Upvotes: 1

Adrian K
Adrian K

Reputation: 4823

I suppose what you want is this:

class Supervisor : Worker by Delegate(){
    
}

Upvotes: 3

Related Questions