6

I am new to Kotlin and am wrestling with the problem of returning immutable versions of internally mutable lists.

I reviewed the following 'Kotlin: Modifying (immutable) List through cast, is it legitimate?' and understand that immutable lists are really just read-only views which do not expose the modification methods.

I want to have a class which exposes an "immutable" List and still want to take advantage of Kotlins automatic getters (without having to provide all the boilerplate for getting the list or a member of the list)

Is the following a bad idea (or will it cause a problem that may be blocked in future releases)

class Foo {
  val names: List<String> = LinkedList;

  fun addName(name: String) {
    (names as LinkedList).add(name)
  }
}

I am looking to allow (for example):

  val foo = Foo;
  println(foo.names.size)

But still prevent the caller from modifying the internals of the class (at least as much as possible). For example removing elements or clearing the backing list.

JWT
  • 143
  • 1
  • 6

2 Answers2

5

The following works:

class Foo {
    private val _names: MutableList<String> = mutableListOf()
    val names: List<String>
        get() = _names.toList()

    fun addName(name: String) {
        _names.add(name)
    }
}

The toList means that if they cast it to a MutableList<String> and try to add to it they will get an UnsupportedOperationException, the _names field holds the real data, and external access is done via the names property

jrtapsell
  • 6,719
  • 1
  • 26
  • 49
  • 1
    Thanks jrtapsell, Yes I know I can maintain this second variable as well... but that wasn't sure if needed the additional clutter.. I think it also works like this: `val name: List = _names` – JWT Oct 14 '17 at 15:53
  • ... although that probably would not prevent the cast – JWT Oct 14 '17 at 15:59
  • The names property just acts as a immutable view of _names, although it would be nice if the clutter could be reduced – jrtapsell Oct 14 '17 at 16:04
  • Using the backing field may simplify the code: [link](https://kotlinlang.org/docs/reference/properties.html#backing-fields) – jrtapsell Nov 06 '17 at 15:43
1

Define mutable list as a private property with underscore (a kind of "field" in Kotlin) and expose it through another public read-only property.

If the "field" is read-only this will do the trick (suggested by @JWT):

class Foo {
    private val _names: MutableList<String> = mutableListOf()
    val names: List<String> = _names

    fun addName(name: String) {
        _names.add(name)
    }
}

If the "field" might be reassigned, it will require to define getter as a function (note var _names):

class Foo2 {
    private var _names: MutableList<String> = mutableListOf()
    val names: List<String>
        get() = _names

    fun addName(name: String) {
        _names.add(name)
    }

    fun reset() {
        _names = mutableListOf()
    }
}

Tests are available here.

Ilya Serbis
  • 21,149
  • 6
  • 87
  • 74