Contents

Kotlin and Null Safety

Kotlin’s type system is designed to eliminate the risk of null reference errors from the code. NullPointerExceptions (NPE) often cause unexpected runtime crashes and application failures. Kotlin aims to prevent this billion-dollar mistake by handling null references at compile-time.

If you’re coming from Java or another language with null references, you’ve likely experienced NullPointerExceptions. Kotlin’s compiler throws a NullPointerException if it detects an unhandled null reference without proceeding with further execution.

Common causes of NullPointerException:
  • Explicitly calling throw NullPointerException()
  • Using the !! operator
  • Uninitialized data, such as passing an uninitialized this reference as an argument
  • Java interoperability issues, such as trying to access a member on a null reference, or using generics with incorrect nullability
Nullable and Non-Nullable Types in Kotlin

In Kotlin, references are either nullable or non-nullable. Non-nullable references cannot hold null values. If you attempt to assign null to a non-nullable reference, the compiler will raise an error.

				
					var s1: String = "Hello"
s1 = null  // Error: compilation issue
				
			

However, to declare a variable that can hold null, we use a nullable type by appending ? to the type:

				
					var s2: String? = "Hello Kotlin"
s2 = null  // No compilation error
				
			

If you want to access the length of a nullable string, you must use safe calls or handle the possibility of a null value:

				
					val length = s2?.length  // Safe call, returns null if s2 is null

				
			

Example: Non-Nullable Type in Kotlin

				
					fun main() {
    var s1: String = "Kotlin"

    println("The length of the string s1 is: ${s1.length}")
}

				
			

Output:

				
					The length of the string s1 is: 6
				
			

In this case, assigning null to s1 would result in a compilation error.

Example: Nullable Type in Kotlin

				
					null
				
			

Output:

				
					var s2: String? = "Hello Kotlin"
s2 = null  // No compilation error
				
			

Here, Kotlin allows assigning null to s2 since it is a nullable type, but accessing its properties requires safe calls.

Checking for null in Conditions

You can use if-else blocks to check if a variable is null:

				
					fun main() {
    var s: String? = "Kotlin"
    
    if (s != null) {
        println("String length is ${s.length}")
    } else {
        println("Null string")
    }

    s = null
    if (s != null) {
        println("String length is ${s.length}")
    } else {
        println("Null string")
    }
}

				
			

Output:

				
					String length is 6
Null string

				
			
Safe Call Operator ?.

The ?. operator simplifies null checks. If the value before the ?. is null, the expression after ?. is not evaluated, and null is returned.

				
					fun main() {
    var firstName: String? = "Alice"
    var lastName: String? = null

    println(firstName?.toUpperCase())  // Output: ALICE
    println(lastName?.toUpperCase())   // Output: null
}

				
			
let() Function with Safe Call

The let() function executes only when the reference is not null:

				
					fun main() {
    var firstName: String? = "Alice"

    firstName?.let { println(it.toUpperCase()) }  // Output: ALICE
}

				
			

Example using let() with Nullable Values

				
					fun main() {
    val stringList: List<String?> = listOf("Hello", "World", null, "Kotlin")
    val filteredList = stringList.filterNotNull()

    filteredList.forEach { println(it) }
}

				
			

Output:

				
					Hello
World
Kotlin

				
			
The Elvis Operator ?:

The Elvis operator returns a default value when the expression on the left is null:

				
					fun main() {
    var str: String? = null
    val length = str?.length ?: -1
    println(length)  // Output: -1
}
				
			

Example using the Elvis Operator

				
					fun main() {
    var str: String? = "Kotlin"
    println(str?.length ?: "-1")  // Output: 6

    str = null
    println(str?.length ?: "-1")  // Output: -1
}
				
			
Not Null Assertion Operator !!

The !! operator forces Kotlin to treat a nullable type as non-null. It will throw a KotlinNullPointerException if the value is null.

				
					fun main() {
    var str: String? = "Kotlin"
    println(str!!.length)

    str = null
    println(str!!.length)  // Throws KotlinNullPointerException
}

				
			

Output:

				
					6
Exception in thread "main" kotlin.KotlinNullPointerException
				
			

Explicit Type Casting

In Kotlin, smart casting allows us to use the is or !is operator to check the type of a variable. Once the type is confirmed, the compiler automatically casts the variable to the desired type. However, in explicit type casting, we use the as operator.

Explicit type casting can be performed in two ways:

  • Unsafe cast operator: as
  • Safe cast operator: as?
  • Unsafe Cast Operator: as : When using the as operator, we manually cast a variable to the target type. However, if the casting fails, it results in a runtime exception. This is why it’s considered unsafe. Example
				
					fun main() {
    val text: String = "This works!"
    val result: String = text as String  // Successful cast
    println(result)
}
				
			

Output:

				
					This works!
				
			

While this example works fine, using the as operator to cast an incompatible type will throw a ClassCastException at runtime.

For instance, attempting to cast an Integer to a String:

				
					fun main() {
    val number: Any = 42
    val result: String = number as String  // Throws exception
    println(result)
}

				
			

Output:

				
					Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String

				
			

Similarly, trying to cast a nullable type to a non-nullable type will result in a TypeCastException:

				
					fun main() {
    val text: String? = null
    val result: String = text as String  // Throws exception
    println(result)
}

				
			

Output:

				
					This works!
				
			

Similarly, trying to cast a nullable type to a non-nullable type will result in a TypeCastException:

				
					fun main() {
    val text: String? = null
    val result: String = text as String  // Throws exception
    println(result)
}

				
			

Output:

				
					Exception in thread "main" kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String
				
			

To prevent this, we should cast to a nullable target type:

				
					fun main() {
    val text: String? = null
    val result: String? = text as String?  // Successful cast
    println(result)
}

				
			

Output:

				
					null
				
			

Safe Cast Operator: as?: Kotlin provides a safer option with the as? operator, which returns null if the casting fails, instead of throwing an exception.

Here’s an example of using as? for safe typecasting:

				
					Safe Cast Operator: as?
Kotlin provides a safer option with the as? operator, which returns null if the casting fails, instead of throwing an exception.

Here’s an example of using as? for safe typecasting:
				
			

Output:

				
					Safe casting
null
99