Can I call a function in Kotlin with a default parameter and force it to be absent?

I have an API which lets someone add an item to an ordered collection and they can optionally provide the index the item should be inserted at. If absent the item should be added to the end.

In my endpoint the json input is unmarshalled into a DTO:

data class AddItemDTO(
    val name: String,
    val insertIndex :Int?
)

In my endpoint I call a service:

myService.insertItem(parentId, addItemDTO.name, addItemDTO.insertIndex)

where parentId is the id of the entity holding the collection I want to add the item to. The parent entity has a function to add an item to the collection:

data class Parent() {
    ... other stuff
    private lateinit var _items: MutableList<Item>

    fun addItem(item: Item, insertIndex: Int = _items.size) {
        _items.add(insertIndex, item)
    }
}

In my service I would like to call this function with the default parameter if it's not null and without it if it is null. My service function now looks like this:

fun insertItem(parentId: UUID, name: String, insertIndex: Int?) {
    val parent = parentRepository.getOne(parentId)
    val item = Item(name)
    if (insertIndex == null) parent.addItem(item)
    else parent.addItem(item, insertIndex)
}

This works fine, but I don't find the if/else very pretty. I was wondering if I could call the method in the parent in another way, something like:

parent.addRound(item, insertIndex ?: void)

Of course I am open to even better / more elegant solutions

1 answer

  • answered 2019-02-10 15:37 Willi Mentzel

    Can I call a function in Kotlin with a default parameter and force it to be absent?

    No, but you can do it like this:

    Since _items is private in Parent, you have no way of knowing what the end index (fallback) would be.

    I would put the logic which decides if an element is inserted at the end or at some other index right inside addItem like this:

    data class Parent() {
        // ...
        private lateinit var _items: MutableList<Item>
    
        fun addItem(item: Item, insertIndex: Int?) {
            _items.add(insertIndex, item ?: _items.size)
        }
    }
    

    This way you can use the elvis operator as you desire.

    insertItem just passes through the insertIndex like this:

    fun insertItem(parentId: UUID, name: String, insertIndex: Int?) {
        val parent = parentRepository.getOne(parentId)
        parent.addItem(Item(name), insertIndex)
    }
    

    Note: You might want to handle what happens if getOne returns null. Since it is a platform type (Parent!), the compiler will not explicitely make you aware of that.