Delegate to class member


#1

Hi,

I personally like the delegation feature of Kotlin a lot. I guess in most cases delegation is a much cleaner designer than inhertiance. So

class Test(map: Map<String, Any?>) : Map<String, Any?> by map

works great. But in some cases, it would be nice to delegate an implemented interface to a member value.
class AnotherTest : Map<String, Any?> by map {      private val map: Map<String, Any?> = HashMap()    }

That would make it possible to really hide the implementation from other objects. Does this make sense?

Best,
Philipp


#2

Your suggestion definitely makes sense, but since teh value of the member may be changing over time, it's a separate feature deserving some special syntax. See thos request: http://youtrack.jetbrains.com/issue/KT-293


#3

Hmm... I don't know how this would be handled internally in Kotlin, but from the outside point of view, I don't see any problem if the member value changes over time. Nullable types should not be allowed here, of cause. But as long as the member has the right type, there will be - by definition - always an object that the call can be delegated to.

I think that would also solve the issue you mentioned. If a get() function is defined for the member value, the delegation target would always be the result of that function. No syntax changes would be needed. But correct me, if I missed a problem here.

Thanks for all the great work.


#4

There are two problems: 1) Calling a delegated function in constructor before the corresponding value is initialized 2) Debugging probelms caused by changing gelegate objects

So we are strongly inclined to make the possibility of these problems visible


#5

I have the same issue and interestingly it’s also with delegating to a Map. My example is passing url-parameters to Retrofit which need to be Map<String,String> so I wrote it like this:

class Filter(towns: Set<String> = setOf(),
             queryParams: MutableMap<String,String>) : Map<String,String> by queryParams {

    init {
        queryParams.put("towns", towns.joinToString(","))
    }
}

For serialization purposes, I would also rather have the queryParams as a delegated or lazy property.

Maybe what would help would be just an AbstractMap class (for Map, not for MutableMap) with just abstract val entries: Pair<K,V> to implement.


#6

I have a similar problem and am curious if there is any better solution than what I did:

interface A {
    fun foo()
}

internal class InternalA : A {
    override fun foo() {}
    internal fun secret() {}
}

open class Helper(a: A) : A by a {
    internal val realA = a as InternalA
}

class B : Helper(InternalA()) {
    fun bar() {
        realA.secret()
    }
}

#7

To be clear, what I would like to do is to remove the Helper class like this:

interface A {
    fun foo()
}

internal class InternalA : A {
    override fun foo() {}
    internal fun secret() {}
}

class B : A by realA {
    val realA = InternalA()
    fun bar() {
        realA.secret()
    }
}

#8

Found a solution using constructors:

interface A {
    fun foo()
}

internal class InternalA : A {
    override fun foo() {}

    internal fun secret() {}
}

class B private constructor(private val a: InternalA) : A by a {

    constructor() : this(InternalA())

    fun bar() {
        a.secret()
    }
}

That should also solve the original question.