Exposing a Mutable member as Immutable


#1

While learning some Android, I came across an interesting scenario that has an official solution, but seems a bit verbose for Kotlin (but still more concise than Java).Consider the following scenario:

There is a controller type class which is responsible for keeping a current set of data. That set of data may change depending on the state of the model. Other classes may need to access that data, but we don’t want them to edit it. Consider the following code as an example:

class ImmutableAccessExample{ 
 
    private val theThingToBeEditedInternally = mutableListOf<String>()
 
    fun theThingToBeAccessedPublicly(): List<String> = theThingToBeEditedInternally
 
    init { 
        theThingToBeEditedInternally.add(0, "something") 
    } 
 
} 

According to official guidelines, the correct implementation would be something like this:

class ImmutableAccessExample{ 
     
        private val _theThing = mutableListOf<String>()
     
        val theThing: List<String>
                get()=_theThing
     
        init { 
           _theThing.add(0, "something") 
        }   
    } 

Is there a less verbose way to do this in Kotlin? My initial thought was something along the lines of this:

val theThing = mutableListOf<String>
    get()=this as List<String>

Obviously, there is a mismatch in the expected return type, and I’m not sure how the compiler would know when to refer to the MutableList since the get() is a List.

Original Context (Android ViewModel)

Regards


#2

In your particular case: private val, the official guideline is a bit more verbose than is needed. As the private val can never change, it only needs to be retrieved once instead of on every access of theThing:

val theThing: List<String> = _theThing

But there is nothing less verbose in Kotlin at the moment. It could not get much shorter anyway, because you have to define an additional “getter” with a different return type. This requires an indication that it is an additional getter, a name for the getter and the type the getter will return.

The shortest I could come up with is this. As you can see this only saves the initializer compared to the code above:

private val _theThing = mutableListOf<String>()
   get theThing: List<String>

I would expect an actual syntax for an additional getter to be a bit more verbose than this minimal example, so the savings would be even less or non-existent.


#3

Thanks for the reply. I’ll continue to use the documentation’s suggestions. I won’t expose the MutableLists because I can still call .MutableList.put on the member. It really isn’t a big deal now that you mention it. Perhaps Kotlin has lulled me into a state of unreasonably high expectations :slight_smile:


#4

There is a common error in your solution: although the theThing appears as List (immutable) at the first blink, its static type is still the MutableList. So one could cast it to override the immutable behaviour:

val v = ImmutableAccessExample()
(v.theThing as MutableList).add("x")
println(v.theThing)    // Prints [something, x]

So be cautious with this approach.


#5

If you take everything that you can do into account, you won’t be able to write software that is fail-safe and foolproof in all cases. If a developer on your team pulls stuff like that, it is best to let them go if you want to prevent a maintenance nightmare in the future.

Never break the contract, neither on the client nor on the server side.


#6

You are generally right. Although, your statement leads to question all safe-code concepts the Kotlin team put a lot of energy in design.
You may not break the contract if you make both side of the code: the API and the use of it. In real life, this is rarely the case, and there are “bad guys” out there who are looking for these opportunities.
You may or may not address the above issue based on the vulnerability of the code, but if you are not aware the security issues of a solution, you may put your code in great danger.
This problem is a good example for possible code injection: if you are not aware, that the public variable you provide is not as immutable as you expected (and you falsely make assumptions that your code is the only place where it can be altered), a “bad guy” may alter its content.

The first step for safe and stable code is to know the possible security issues. To rephrase your motto: “Never break the contract and prevent it to be broken!”


#7

I think the real confusion here is that people still think of List as immutable, which it is not. The List interface describes a read only list, not an immutable one.
If you are dealing with critical data which must not be changed in any situation I suggest using a library which actually creates immutable lists.
That being said I agree with jstuyts. There are many ways that you can change seemingly immutable data if you really want to. You can modify data using reflection or just cast a List to MutableList. In general this is not really a problem, as long as you don’t handle security relevant data. Most times the worst that can happen is that the app crashes.

I don’t think so. The safe-code concepts in kotlin are not there to prevent you from creating unsafe code. Thats impossible. They are there to prevent you from doing so accidentally. There are ways of setting non null properties to null creating NPEs in Kotlin. This does not mean the nullability system is unnecessary.

Agreed, you should be aware. But you don’t seem to be. Otherwise you would not call List to be immutable. The problem is not that “a bad guy” could cast List to MutableList. It’s that people assume that List is immutable.


#8

This is how I do it:

// public type of the property is List, i.e. not mutable:
val foos: List<String> = mutableListOf()

fun internalModifier() {
    foos as MutableList
    // from here on we can use foos as a MutableList

    foos.add("foo")
    foos.add("bar")
}

#9

Thanks! Now its much cleaner. I falsely interpreted “something, that can be set only in construction time and has only non-altering functions” as immutable. Now it’s clear, that I can take the List interface as simply a reduced view on a mutable list (which is reasonable, because the backing Java ArrayList is mutable), which only provides read access to the list. And the same stands with the val fields as general: I must be much more careful not to make too high assumption on the immutability of a value. (This article made my view clearer: https://blog.danlew.net/2017/05/30/mutable-vals-in-kotlin/)

For me, the main conclusion was that val and read-only collections only prevent accidental alteration of the data, but doesn’t prevent for intentional intervention. Now, that it is clear, I could use these tools as they meant to be used. :slight_smile:

On the other hand, when I really have to make a collection immutable, there is still the java Collections utilities to wrap them.

One last thought: This stands in the official Kotlin guide (https://kotlinlang.org/docs/reference/collections.html):

Currently, the listOf method is implemented using an array list, but in future more memory-efficient fully immutable collection types could be returned that exploit the fact that they know they can’t change.

This proves that you are absolutely right, at the moment. It also proves that thinking that List is immutable is not far from the intention of the developers and there may be a really immutable list in the future.


#10

List is never going to mean that it is immutable, because that it is not backward compatible and is extremely hard to enforce. And even if it was possible to enforce, it will break lots of existing code.

What may happen is that some functions (like listOf(...)) currently returning mutable lists (which you cannot mutate unless you cast/reflect/…) start returning other List implementations. Some of these implementations could be immutable.


#11

How will it break existing code? Unlike java.util.List, it does not even have mutating functions. Even with the Java List interface we can enforce true immutability by wrapping the list in a Collections.unmodifiableList(...).

This is a common practice in Java, and it does not cause many problems. It will be even more sound in kotlin.


#12

I meant that it will break existing mutable implementations if Kotlin would start enforcing immutability of all implementers of List.


#13

I see that makes sense now.