Null Unions

FILE : NullUnions.kt

  • How kotlin represents nullable values
  • How compiler uses null information
  • Operators for null unions
  • An example of custom union implementation

Null or Type

In Java Nullpointer exception is a common thing because both :

String s1="string1";
String s2=null;

Have the same type String.

In Kotlin situation is different. We have two different types String and String? where

String? = String or null

or in general
Type? = Type or null

We have an alternative of two technically different types so we can call this an union of null and some type

Type Safety

What is an advantage of such union? Because technically String and String? are different types so you can not pass null when only non null types are expected

 fun naiveUpper(input: String) = input.toUpperCase()
//    naiveUpper(sOrNull) //compilation error
//    val s:String=sOrNull //compilation error
//    val s:String=null  //compilation error

Union operators

  • Safe Traversal '?.'

You can call methods on potentially nullable reference without triggering nullpointer exception. The result is still null union.

val sOrNull: String? = ...
val unionResult2: String? = sOrNull?.toUpperCase()?.trim()?.decapitalize()
  • folding '?:'

If you want to move from union level to simple value level you just need to provide an alternative for null

 val foldResult1: String =sOrNull?.toUpperCase() ?: ""
  • unsafe get '!!'

in those 0,00001 % cases when you may have better context knowledge that type system

Displayer.section("nuclear option",naiveUpper(sOrNull!!))

From casting to null union

Kotlin has richer casting mechanism than Java. Beside standard Any as Type we also have Any as? Type .

Second option allow us to continue computation on null unions level which is very similar to how we would write program with Option in more functional style

variable as? String?.toUpper() ?: "ALTERNATIVE"

Modeling Union

By using Kotlin mechanism of sealed trait we can create our own model of union types.

sealed abstract class NullUnion{
    abstract fun safeCall(call:(String) -> String): NullUnion

    companion object {
        fun of(input:String?):NullUnion = if(input==null) NullValue else NonNullValue(input)
    }
}

class NonNullValue (private val v:String) : NullUnion(){
    override fun safeCall(call:(String) -> String): NullUnion = NonNullValue(call(v))
    override fun toString(): String {
        return "NonNullValue(v='$v')"
    }

}

object NullValue : NullUnion(){
    override fun safeCall(call: (String) -> String): NullUnion  = this
    override fun toString(): String ="NullValue"

}

Here we are using standard subtype polimorphism to implement proper operators for specific union part. Because of sealed modificator we can easily control number of subtypes and set of operations between them.

Questions

At the bottom of the file you can find some questions to practice gained knowledge.

1) Which line displays description if it is present or text "NO DESCRIPTION" ?

class Product(val name:String,val price:Int,val description:String?)
val p = Product("PC",200,null)

a) p :? "NO DESCRIPTION"

b) p?.description?.or("NO DESCRIPTION")

c) p?.description ?: "NO DESCRIPTION"

2) what needs to be here nistead of '???'

val n:Any = "123"

???{ //what needs to be here istead of '???'

println(n.toInt())

}

3) which type is more similar to lang.java.object ?

a) Any

b) Any?

EXERCISES

FILE : NullUnionsExercises.kt

There are two short exercises in this module . In first one you need to implement methods which uses only NullStringUnion types

 fun join(prefix: String?, middle: String?, suffix: String?): String

use tests as a specification.

In the second exercise you will work with custom made union types. You need to finish implementation of couple operators for this type.

results matching ""

    No results matching ""