Kahibaro
Discord Login Register

3.4 Null Safety

Why Null Safety Matters in Kotlin

Null values are a common source of crashes in many programming languages. In older Android apps written in Java, a very common error is NullPointerException. Kotlin was designed to drastically reduce these errors by making null handling explicit and part of the type system.

In Kotlin, every variable type is either non nullable or nullable. This is the foundation of Kotlin null safety. Understanding and using this correctly is essential for stable Android apps, because you often deal with data that might be missing, delayed from the network, or not yet initialized.

Nullable and Non Nullable Types

By default, Kotlin types are non nullable. This means a variable of type String cannot hold null. If you try, the compiler will show an error before you even run the app.

To allow null, you must explicitly mark the type as nullable with a question mark.

var nonNullableText: String = "Hello"
// nonNullableText = null          // Compilation error
var nullableText: String? = "Hello"
nullableText = null               // OK

A non nullable type guarantees there is always a value. A nullable type explicitly allows null, and the compiler forces you to handle that possibility wherever you use it.

Important rule:
If a type ends with ?, like String?, you must treat it as possibly null every time you access it. The compiler will not let you ignore that.

This clear distinction lets you see, just by reading the type, whether a value can be missing. This is especially important when you work with Android APIs, user input, and data from the network.

Safe Calls with `?.`

When you have a nullable variable, you cannot directly access its properties or functions. The compiler blocks this to prevent crashes.

For example, this is not allowed:

var name: String? = "Alice"
// val length = name.length    // Compilation error

To safely access members of a nullable value, you use the safe call operator ?.. A safe call performs the access only if the variable is not null. If it is null, the whole expression becomes null instead of crashing.

var name: String? = "Alice"
val length: Int? = name?.length   // length is 5
name = null
val length2: Int? = name?.length  // length2 is null, no crash

Safe calls are often chained when you navigate through multiple nullable references.

val countryCode: String? = user?.address?.country?.code

If any part of the chain is null, the expression evaluates to null. This style is very useful when dealing with data that may be partially missing. In Android you will see this pattern often when you access views, extras, or data from responses that might not be present.

The Elvis Operator `?:`

Safe calls usually produce a nullable result. Sometimes you want to provide a default value when the result is null. The Elvis operator ?: is used to replace null with a fallback.

The expression a ?: b means: if a is not null, use a. Otherwise, use b.

val name: String? = null
val displayName: String = name ?: "Guest"
// displayName is "Guest"

You can combine safe calls and the Elvis operator to handle nullable values in a concise way.

val userName: String? = user?.name
val greeting = "Hello, " + (userName ?: "Guest")

This is especially useful in Android when you are showing text to the user and want a sensible default when real data is missing, for example when loading data from shared preferences or from intents.

Important pattern:
Use ?. to safely access a nullable value.
Use ?: to provide a default when the result is null.

Smart Casts with Null Checks

Kotlin can often automatically treat a nullable variable as non nullable after you check that it is not null. This is called a smart cast.

When you write an explicit check, Kotlin understands that inside that branch, the variable cannot be null.

val text: String? = "Hello"
if (text != null) {
    // Inside this block, text is treated as String, not String?
    val length = text.length
}

You can use other null checks as well, like == null with else branches, and Kotlin will smart cast the variable inside the relevant block.

This pattern appears frequently in Android code, for example when you check if a bundle or intent extra exists before using it.

val bundle = intent.extras
if (bundle != null) {
    // bundle is smart cast to Bundle
    val id = bundle.getString("user_id")
}

You will see more Android specific versions of this pattern in later chapters. Here, focus on the idea that the compiler helps you by remembering your null checks.

The Not Null Assertion `!!`

Sometimes you know a value is not null, but the compiler cannot be sure. In that case, Kotlin lets you use the not null assertion operator !!.

The expression value!! tells Kotlin: "I am certain value is not null. Treat it as non nullable." If value is actually null at runtime, the app will crash with a KotlinNullPointerException.

val text: String? = getTextOrNull()
val length: Int = text!!.length    // Crashes if text is null

Although this may seem convenient, in Android apps overuse of !! leads back to the same kind of runtime crashes Kotlin tries to avoid.

Important rule:
Avoid !! whenever possible.
Prefer safe calls ?., the Elvis operator ?:, or proper null checks.
Use !! only when you are absolutely sure a value cannot be null and there is no reasonable way to express that to the compiler.

Many Android tutorials and older examples use !! with views or intent extras. In modern Kotlin code, it is better to design your code to avoid it, for example by checking for null or by restructuring your logic.

Safe Casts with `as?`

Kotlin also has safe casts, which relate to both type casting and null safety. When you try to convert a value from one type to another, an unsafe cast with as throws an exception if the cast is not possible.

A safe cast with as? returns null instead of throwing an exception if the cast fails. This combines naturally with nullable types.

val value: Any = "Hello"
val text1: String? = value as? String   // text1 is "Hello"
val number: Int? = value as? Int        // number is null, no crash

This pattern is useful in Android when you get generic objects, such as from bundles, arguments, or external libraries, and you are not completely sure about the type. Using as? keeps your code safer and integrates with the existing null handling tools like ?:.

Nullable Types in Function Parameters and Returns

Null safety applies to function parameters and return types in the same way as variables.

If a function parameter is non nullable, the function can assume it is always present and does not need to perform null checks.

fun printLength(text: String) {
    println(text.length)      // No null checks needed
}

If a function parameter is nullable, the function must handle the null case.

fun printLengthSafe(text: String?) {
    if (text != null) {
        println(text.length)
    } else {
        println("No text")
    }
}

The same idea applies to return types.

fun findUserName(id: Int): String? {
    // If the user is not found, return null
    return null
}

When you call findUserName, the compiler forces you to handle the nullable result, using safe calls, null checks, or the Elvis operator.

This pattern is very important in Android to represent "no data" or "not found" clearly instead of using special placeholder values.

Working with Java and Platform Types

Android is built on top of Java, and many Android APIs are written in Java. When Kotlin interacts with Java code, the compiler may not always know whether a value can be null or not. These uncertain values are called platform types.

A platform type comes from Java, and the Kotlin compiler treats it in a more relaxed way. It allows certain operations that might be unsafe, because Java did not specify nullability.

You might see platform types in error messages or hints, such as String!. You cannot write this ! syntax yourself, but it indicates that Kotlin is not sure if this type can be null.

Because of platform types, you must be especially careful when calling Java based Android APIs. It is often a good idea to immediately wrap such values in safer Kotlin constructs.

For example, if an Android API can return null, treat it as nullable in your own code.

// Suppose getStringExtra comes from Java and might return null
val id: String? = intent.getStringExtra("user_id")

By declaring id as String?, you bring the value into the Kotlin null safety system and force yourself to handle it properly. This is an important habit in Android apps where many APIs come from Java.

Summary of Null Safety Tools

Kotlin null safety gives you several tools that work together.

Nullable types use ? in the type, such as String?. Safe calls ?. let you call methods or access properties on nullable values without crashing. The Elvis operator ?: provides a default value when an expression is null. Smart casts allow the compiler to treat a nullable variable as non nullable after a null check. The not null assertion !! forces a nullable reference to be treated as non nullable but crashes at runtime if it is actually null. Safe casts as? let you attempt a cast and receive null instead of an exception if it fails.

In Android development these tools are central for working with data that can be missing, delayed, or optional. As you continue, you will apply them when dealing with user input, intents, views, network responses, and many other parts of a real app.

Views: 1

Comments

Please login to add a comment.

Don't have an account? Register now!