32

Kotlin enables me to implement an interface by delegating to a primary constructor argument like so:

class Foo(xs : ArrayList<Int>) : List<Int> by xs { }

But this exhibits the backing implementer to the user. Delegating to an anonymous also seems to be ok:

class Foo() : List<Int> by ArrayList<Int>() { }

This hides the implementation details, but we loose access to features not provided by the interface, which in this case is mutability.

I would therefore like to delegate the implementation to a property that is not in the primary constructor. What I would like to have is similar to

class Foo() : List<Int> by xs {
    val xs : List<Int> = ArrayList<Int>()
}

which doesn't compile.

Is it possible to have a property defined explicitly in the class body and still be able to delegate implementation to it?

2 Answers 2

20

This is not currently possible. The expression in the by-clause is computed only once before the construction of the class, so you cannot reference symbols of that class.

There is a request in the issue tracker to allow this, although it's almost definitely not going to be supported in Kotlin 1.0.

One funny workaround that sometimes works is to make the property which you want to be a delegate, a constructor parameter with the default value instead. That way it'll be accessible both in the by-clause and in the class body:

class Foo(val xs: List<Int> = ArrayList<Int>()) : List<Int> by xs {
    fun bar() {
        println(xs)
    }
}

Keep in mind though that xs in by xs is still calculated only once here, so even if xs is a var property, only the default value provided in the constructor will be used. It's not a universal solution, but sometimes it can help.

Sign up to request clarification or add additional context in comments.

Comments

12

Expanding on the answer of Alexander Udalov, I came up with a solution using a private base class

private open class FooBase(protected val xs : MutableList<Int>) : List<Int> by xs { }

class Foo() : FooBase(ArrayList()) {
    fun bar() {
        xs.add(5)
    }
}

Now I can have access to the property backing my interface implementation but am not restricted to operations provided by that interface while still hiding the actual implementation from the user.

Note: Although it works, I get the following warning from IntelliJ IDEA 15 CE which arises from EXPOSED_SUPER_CLASS inspection: Deprecated: subclass effective visibility 'public' should be the same or less permissive than its superclass effective visibility 'private'. I'm not quite sure what the deprecated part here means – whether the warning will be removed in the future or this won't compile at some point. Anyway, we don't really have to use a private open class, abstract or simply open will do, because even if the user is allowed to create an instance of FooBase, there is not much he can do with it.

Update:

There is actualy a simple and compact solution that does not use any suspicious behaviour:

class Foo private constructor(private val xs: ArrayList<Int>) : List<Int> by xs {
    constructor() : this(ArrayList<Int>()) { }
}

1 Comment

I'm curious about why this deprecation happened. I receive the same in cases like this. What is the motivation?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.