Kotlin Null Safety Basics
Null safety is one of Kotlin’s most important features. It helps prevent the dreaded NullPointerException (NPE) by making null handling explicit and safe. Let’s explore how Kotlin handles null values and how to use its null safety features effectively.
Nullable Types
In Kotlin, types are non-null by default:
// Non-nullable String
var name: String = "John"
name = null // Compilation error
// Nullable String
var nullableName: String? = "John"
nullableName = null // OK
Safe Call Operator (?. )
val name: String? = null
val length = name?.length // Returns null instead of throwing NPE
// Chain of safe calls
val street = user?.address?.street
Elvis Operator (?:)
// Basic usage
val name: String? = null
val length = name?.length ?: 0
// With function calls
fun getLength(str: String?) = str?.length ?: 0
// With throw
val name = nullableName ?: throw IllegalArgumentException("Name required")
Not-null Assertion (!!)
val name: String? = "John"
val length = name!!.length // Throws NPE if name is null
// Use with caution
try {
val length = nullableName!!.length
} catch (e: NullPointerException) {
println("Name was null")
}
Safe Casts
// Safe cast operator
val any: Any = "Hello"
val str: String? = any as? String
// Traditional cast
val str2: String = any as String // Throws ClassCastException if cast fails
Null Safety in Collections
// List of nullable elements
val list: List<String?> = listOf("a", null, "c")
// Filter nulls
val nonNullList = list.filterNotNull()
// Map with null safety
val lengths = list.map { it?.length ?: 0 }
Platform Types
When working with Java code:
// Java
public class JavaClass {
public String getValue() { return null; }
}
// Kotlin
val javaClass = JavaClass()
val value = javaClass.value // Platform type
val length = value?.length // Safe call recommended
Null Safety Best Practices
-
Prefer non-null types
// Good fun processName(name: String) { } // Avoid fun processName(name: String?) { }
-
Use safe calls by default
// Good val length = name?.length // Avoid val length = name!!.length
-
Provide default values
// Good val length = name?.length ?: 0 // Avoid val length = if (name != null) name.length else 0
Common Patterns
Early Returns
fun processUser(user: User?) {
if (user == null) return
// Safe to use user here
println(user.name)
}
Let Function
val name: String? = "John"
name?.let {
// Only executed if name is not null
println("Name is $it")
}
Also Function
val list = mutableListOf<String>()
list.also {
it.add("Hello")
it.add("World")
}
Null Safety in Android
// View binding
private var _binding: ActivityMainBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
Conclusion
Kotlin’s null safety features help you:
- Prevent NullPointerExceptions
- Write more reliable code
- Make null handling explicit
- Improve code readability
Remember:
- Use nullable types when a value can be null
- Prefer safe calls over not-null assertions
- Provide default values when appropriate
- Handle null cases explicitly
Stay tuned for our next post where we’ll explore Kotlin data classes!