Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin: exposing immutable list in API

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.

like image 332
JWT Avatar asked Oct 14 '17 00:10

JWT


2 Answers

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

like image 162
jrtapsell Avatar answered Nov 19 '22 04:11

jrtapsell


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.

like image 24
Lu55 Avatar answered Nov 19 '22 04:11

Lu55