Kotlin null Keyword
last modified April 19, 2025
Kotlin's null safety system helps eliminate null reference exceptions. The
null
keyword represents the absence of a value. This tutorial
explores null handling in Kotlin with practical examples.
Basic Definitions
In Kotlin, types are non-nullable by default. To allow null values, you must
explicitly declare a type as nullable using ?
. The null
keyword represents a null reference. Kotlin provides several operators to work
with null values safely.
Nullable Types Declaration
To declare a variable that can hold null, append ?
to its type. This
makes the type nullable. Without ?
, the variable cannot hold null.
package com.zetcode fun main() { var name: String = "Kotlin" // name = null // Compilation error var nullableName: String? = "Kotlin" nullableName = null // Valid println(nullableName) // Output: null }
Here name
cannot be null, while nullableName
can. The
commented line would cause a compilation error. This demonstrates Kotlin's null
safety at compile time.
Safe Calls Operator (?.)
The safe call operator ?.
allows you to safely access properties or
methods of nullable objects. If the object is null, the expression returns null
instead of throwing an exception.
package com.zetcode fun main() { val str: String? = null val length = str?.length println(length) // Output: null val str2: String? = "Hello" println(str2?.length) // Output: 5 }
The first safe call returns null because str
is null. The second
call returns the length because str2
contains a string. This
prevents NullPointerException.
Elvis Operator (?:)
The Elvis operator ?:
provides a default value when a nullable
expression is null. It's similar to the ternary operator in other languages but
specifically for null checks.
package com.zetcode fun main() { val name: String? = null val length = name?.length ?: 0 println(length) // Output: 0 val name2: String? = "Kotlin" println(name2?.length ?: 0) // Output: 6 }
When name
is null, the Elvis operator returns 0. When name2
has a value, it returns the string length. This provides a safe fallback for null
values.
Not-null Assertion (!!)
The not-null assertion operator !!
converts any value to a non-null
type. If the value is null, it throws a NullPointerException. Use this only when
you're certain the value isn't null.
package com.zetcode fun main() { val str: String? = "Kotlin" println(str!!.length) // Output: 6 val str2: String? = null // println(str2!!.length) // Throws NullPointerException }
The first assertion succeeds because str
isn't null. The commented
line would throw an exception because str2
is null. This operator
should be used sparingly.
Safe Casts with as?
The safe cast operator as?
attempts a cast and returns null if it
fails. This combines null safety with type casting, preventing
ClassCastException.
package com.zetcode fun main() { val obj: Any = "Kotlin" val str: String? = obj as? String val num: Int? = obj as? Int println(str) // Output: Kotlin println(num) // Output: null }
The first cast succeeds because obj
is a String. The second fails
but returns null instead of throwing an exception. This is safer than regular
casting.
let Function with Nullable
The let
function executes a block only if the object isn't null. It's
useful for performing operations on nullable objects safely.
package com.zetcode fun main() { val name: String? = "Kotlin" name?.let { println("Name length is ${it.length}") // Output: Name length is 6 } val nullName: String? = null nullName?.let { println("This won't be printed") } }
The first let
block executes because name
isn't null.
The second block doesn't execute because nullName
is null. This
pattern is common in Kotlin for null-safe operations.
Late-initialized Properties
The lateinit
modifier allows non-null properties to be initialized
later. This is useful when dependency injection or test setup provides the value.
package com.zetcode class User { lateinit var name: String fun initialize() { name = "Kotlin" } fun printName() { if (::name.isInitialized) { println(name) } } } fun main() { val user = User() // println(user.name) // Throws UninitializedPropertyAccessException user.initialize() user.printName() // Output: Kotlin }
The name
property is declared without null but initialized later.
Accessing it before initialization throws an exception. The isInitialized
check verifies initialization status.
Best Practices for Null Safety
- Prefer non-null types: Design with non-null types when possible to avoid null checks.
- Use safe calls: Favor
?.
over!!
for safer code. - Provide defaults: Use the Elvis operator to provide sensible defaults for null values.
- Consider lateinit: For properties initialized later, use
lateinit
instead of nullable types. - Leverage let: Use
let
for null-safe scope functions.
Source
Kotlin Null Safety Documentation
This tutorial covered Kotlin's null
keyword and null safety features
in depth. We explored nullable types, safe calls, Elvis operator, and more. Proper
null handling makes Kotlin code more robust and less prone to runtime exceptions.
Author
List all Kotlin tutorials.