Accessing Kotlin field without running it's getter

Viewed 74

I have a class that holds a MutableList and the class makes changes to that list. I defined a getter to allow outside modules to get a copy of the list. Problem is whenever i access the field within the class, I end up changing the list copy rather than the actual list. How can I access the list without going through the getter inside the defined class? Is this considered bad practice, if so why?

class Server(private val port: Int): Runnable {

    val clients = mutableListOf<SocketChannel>()
        get() = field.toMutableList()


    private val sel = Selector.open()
    private val serverChannel = NIOServerChannel(port, sel).get()
    private val engine = ServerEngine(sel, clients)

    private val acceptor = ChannelAcceptor(serverChannel, clients)
    private val reader = ChannelReader()
    private val writer = ChannelWriter()
    private val middleware = Middleware()

    override fun run() {
        buildEngine()
        println("Starting server on port: $port")
        engine.run()
    }

    fun use(func: (String, Response) -> Unit) = middleware.add(BiConsumer(func))

    private fun buildEngine() {
        reader.process = middleware.get()

        engine.add(acceptor)
        engine.add(reader)
        engine.add(writer)
    }
}
4 Answers

The problem is this line: get() = field.toMutableList()

Look at the definition of toMutableList: it creates a new collection. Just return the field or omit the getter at all.

There doesn't appear to be any convention for accessing an automatically-created backing field. The Kotlin docs on backing fields declare an explicit backing property:

private val _clients = mutableListOf<SocketChannel>()
val clients: MutableList<SocketChannel>
        get() = _clients.toMutableList()

Note: I'm not a fan of this syntax since it obliges you to remember which fields you exposed (to access them via _ prefixed names). I would probably use clientsInternal, so my IDE can recommend it and I can select without backtracking to add the _.

If your access is single-threaded, I think you could avoid creating copies by doing this:

private val clientsList = mutableListOf<SocketChannel>()

val clients: List<out SocketChannel>
    get() = clientsList as List<out SocketChannel>
@JvmField
val clients = mutableListOf<SocketChannel>()

fun getClients() = clients.toMutableList()
Related