'Is there a way to use the default value on a non-optional parameter when null is passed?

For example, if I have the following data class:

data class Data(
    val name: String = "",
    val number: Long = 0
)

And functions that can return null:

fun newName(): String? {}

fun newNumber(): Long? {}

I know I can use the following to use the value of the functions if they are not null:

val newName = newName()
val newNumber = newNumber()

val data = Data(
        if (newName != null) newName else "",
        if (newNumber != null) newNumber else 0
)

But is there a way to just use the default value specified in the constructor of the Data class when the values are null?

I could not find anything in the documentation, but I was hoping something like this would work:

val data = Data(newName()?, newNumber()?)

But that does not compile.



Solution 1:[1]

You can define a companion object for your data class and overload its invoke operator to use default values when null is passed:

data class Data private constructor(
    val name: String,
    val number: Long
) {
    companion object {
        operator fun invoke(
            name: String? = null,
            number: Long? = null
        ) = Data(
            name ?: "",
            number ?: 0
        )
    }
}

Solution 2:[2]

the secondary constructor only supports for the Nullable primitive properties. which means it will result in 2 same constructors if the property is not a primitive type, for example:

data class Data(val name: String) {
    constructor(name: String? = null) : this(name ?: "foo");
    // ^--- report constructor signature error                
}

data class Data(val number: Long = 0) {
     constructor(number: Long? = null) : this(number ?: 0)
     //                  ^--- No problem since there are 2 constructors generated:
     //                       Data(long number) and Data(java.lang.Long number)
}

an alternative way is using invoke operator for that, for example:

data class Data(val name: String) {
    companion object {
        operator fun invoke(name: String? = null) = Data(name ?: "")
    }
}

IF the class is not a data class, then you can lazy initializing properties from parameters, rather than define properties on the primary constructor, for example:

class Data(name: String? = null, number: Long? = null) {
    val name = name ?: ""
    val number = number ?: 0
}

Solution 3:[3]

If needed, I can offer another solution:

data class Data(
    val inputName: String?,
    val inputNumber: Long?
) {
    private val name = inputName ?: ""
    private val number = inputNumber ?: 0
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 holi-java
Solution 3 ympakt