Home > Software engineering >  Set a value to builder if not null - Kotlin
Set a value to builder if not null - Kotlin

Time:06-10

I am new to Kotlin. Can someone tell me how can I set the value only if not null here in below cade. There should be a way to use with let() but I am not sure how to do it.

If the var2 is not null only I should set it. Otherwise null pointer error will throw.

private fun myFunc(var1: Type1 , var2: Type2?) {
    
        val request = class1.newBuilder()
            .setType1(var1)
            .setType2(var2) // how to set var2 only if not null? 
            .build()
        clientClass.send(request)
 }

CodePudding user response:

If each builder function returns a new Builder instance, use run:

private fun myFunc(var1: Type1 , var2: Type2?) {
    val request = class1.newBuilder()
        .setType1(var1)
        .run { if(var2 != null) setType2(var2) else this }
        .build()
    clientClass.send(request)
}

If the builder functions mutate and return the same Builder instance, it’s simpler to use apply

private fun myFunc(var1: Type1 , var2: Type2?) {
    val request = class1.newBuilder()
        .setType1(var1)
        .apply { if(var2 != null) setType2(var2) }
        .build()
    clientClass.send(request)
}

// or more cleanly using apply for everything instead of chaining:

private fun myFunc(var1: Type1 , var2: Type2?) {
    val request = class1.newBuilder().apply {
        setType1(var1)
        if(var2 != null) setType2(var2)
        build()
    }
    clientClass.send(request)
}

Example of a Builder class whose functions return new instances:

fun setType2(type2: Type2): Builder {
    return CombinedBuilder(this, type2) // a new object
}

Example of a Builder class whose functions return the same instance:

fun setType2(type2: Type2): Builder {
    this.type2 = type2
    return this // the same object
}

The second type is more common, but sometimes the first type is used. You might have to check the source code to know for sure. If you can't be sure, use the .run method because it will work for either.

CodePudding user response:

Using scope functions is one way to solve this but results in code that is hard to read imo. If you stick to the builder pattern, the simplest solution would be to modify the builder to accept Null values:

class Builder {
    private var type1: Type1 = <default>
    private var type2: Type2 = <defaul>

    fun setType1(type: Type1?): Builder {
        if (type != null) this.type1 = type
        return this
    }
    fun setType2(type: Type2?): Builder {
        if (type != null) this.type2 = type
        return this
    }
}

That way you can keep:

class1.newBuilder()
    .setType1(var1)
    .setType2(var2)
    .build()

To communicate the fact that the values won't be set if they are Null, change the name of the setters to e.g. setTypeIfNotNull.

With Kotlin the builder pattern is kind of obsolete (unless you create a DSL) because you can use a constructor with default and named arguments instead, something like:

class Request(val type1: Type1 =  <default>, val type2: Type2 = <default>)

Then you can do:

Request(
    type1 = type1,
    type2 = type2,
)

Now this doesn't cover the Null case but you can use this to accept null values:

companion object {
    public operator fun invoke(type1: Type1, type2: Type2? = null) = Request(type1, type2 ?: <default value>)
}

The reason why there's a companion object instead of a constructor is explained here: Is there a way to use the default value on a non-optional parameter when null is passed?.

  • Related