Property with different public signature than private


#1

A situation I hit quite frequently that I think has the potential to be prettier is that I often have a property that I want to expose as public with a different type than how I use it as private or protected.

How I do this now:

private val _foo: MutableList<Float> = ArrayList()
val foo: List<Float>
  get() = _foo

It’s not that that code is all that ugly, it’s that this is such a common case where I want to expose a property in an immutable and more general way than how it’s used internally that I have examples of that absolutely everywhere in my codebase and if that were two lines instead of 3 it would make my life 33% happier :slight_smile:


#2

There is a KEEP about this:


#3

Beautiful, thanks!
Reading the KEEP, that’s exactly what I wanted :slight_smile:


#4

Let’s hope it will be part of Kotlin at some time.


#5

Have you looked into Delegated Properties? I think you could create either a universal one to cover all similar cases, or create one specifically around lists. I’ll try to remember to try it out when I get time and update this.


#6

Yeah, but that doesn’t change the member type. You could access the delegate and grab the mutable property it wraps, but that wouldn’t really be any cleaner. As far as I know a property delegate can’t split visibility.


#7

Right, you’d still have the private one. The delegated property is on the public one and works with the private one.
That’s the part I have to check whether it works.


#8

like this:

class Redirect<T>(val source: KMutableProperty<out List<T>>){
    operator fun getValue(thisRef: Any?, property: KProperty<*>): List<T> {
        return source.getter.call(thisRef)
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: List<T>) {
        source.setter.call(thisRef, ArrayList<T>(value))
    }
}

class User (list: MutableList<Int>) {
    private var _list = list
    public val outList: List<Int> by Redirect(User::_list)
}

I tried this on try.kotlinlang.org and it compiled fine, but it failed with an exception:

Exception in thread “main” java.lang.IllegalStateException: No BuiltInsLoader implementation was found. Please ensure that the META-INF/services/ is not stripped from your application and that the Java virtual machine is not running under a security manager

which makes it sound like maybe the site doesn’t have the full reflection access. Try it out and let me know. I won’t likely get a chance to try it out properly on IntelliJ until Monday

Here’s a “universal” one, btw:

class Redirect <Private, Public> (
    val source: KMutableProperty<Private>,
    val transUp: (Private) -> Public,
    val transDown: (Public) -> Private) {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): Public {
        return transUp(source.getter.call(thisRef))
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Public) {
        source.setter.call(thisRef, transDown(value))
    }
}

#9

And obviously, you can make a read-only version so that the transDown argument isn’t needed. Or you can make it nullable with a default to null and hope people aren’t dummies.


#10

Sorry, I’m not really following you.
Why would you go through all that trouble of the delegate over just doing this:

class User (private val mutableList: MutableList<Int>) {
    public val list: List<Int> = mutableList
}

The KEEP that wasabi linked was a very succinct discussion of the problem, without such a feature however the next best thing is simply two properties pointing to the same value but with different access.


#11

Except your User class here doesn’t make it possible to make the private property be a var. If you used a var instead of a val and reassigned the private one, it wouldn’t change what the public one returns.

What you’re asking for is a way to define the public property without having to rewrite the getter (and possibly setter) every time. Well, that’s exactly what the Delegated Property does.


#12

Alas, it doesn’t work because reflection didn’t let me get around it being private.