Kotlin Null Safety
Use when kotlin's null safety system including nullable types, safe calls, Elvis operator, smart casts, and patterns for eliminating NullPointerExceptions while maintaining code expressiveness and clarity.
What this skill does
# Kotlin Null Safety
## Introduction
Kotlin's null safety system eliminates NullPointerExceptions at compile time by
distinguishing between nullable and non-nullable types in the type system. This
approach makes null handling explicit and forces developers to consciously
handle potential null values.
Unlike Java where any reference can be null, Kotlin requires explicit
declaration of nullability with the `?` operator. The compiler enforces null
checks before dereferencing nullable values, preventing the vast majority of
null-related crashes that plague Java applications.
This skill covers nullable types, safe call operators, smart casts, nullability
in generic types, and patterns for designing null-safe APIs while maintaining
code clarity.
## Nullable Types
Nullable types explicitly indicate that a variable or property can hold null,
while non-nullable types provide compile-time guarantees of non-null values.
```kotlin
// Non-nullable types
var name: String = "Alice"
// name = null // Compilation error
// Nullable types
var nullableName: String? = "Bob"
nullableName = null // OK
// Function parameters
fun greet(name: String) {
println("Hello, $name")
}
fun greetNullable(name: String?) {
if (name != null) {
println("Hello, $name")
} else {
println("Hello, guest")
}
}
// greet(null) // Compilation error
greetNullable(null) // OK
// Nullable return types
fun findUser(id: Int): User? {
return if (id > 0) User(id, "Alice") else null
}
data class User(val id: Int, val name: String)
// Nullable properties
class Person(
val name: String,
val email: String?,
var phoneNumber: String?
)
// Collections with nullable elements
val nullableList: List<String?> = listOf("A", null, "B")
val listOfNullable: List<String>? = null
// Platform types from Java (String!)
// Treated as nullable for safety in Kotlin
// Nullable this
class Service {
fun process() {
val self: Service? = this
self?.validate()
}
fun validate() {}
}
```
The `?` suffix makes a type nullable. Non-nullable types cannot be assigned null
without explicit nullability declaration, preventing accidental null references.
## Safe Call Operator
The safe call operator `?.` safely accesses properties and methods on nullable
references, returning null if the receiver is null instead of throwing NPE.
```kotlin
// Basic safe calls
val name: String? = "Alice"
val length: Int? = name?.length
val nullName: String? = null
val nullLength: Int? = nullName?.length // Returns null
// Chaining safe calls
data class Address(val street: String?, val city: String?)
data class Company(val address: Address?)
data class Employee(val company: Company?)
val employee: Employee? = Employee(Company(Address("Main St", "NYC")))
val city: String? = employee?.company?.address?.city
println(city) // "NYC"
val nullEmployee: Employee? = null
val nullCity: String? = nullEmployee?.company?.address?.city
println(nullCity) // null
// Safe calls with methods
fun processUser(user: User?) {
user?.let { u ->
println("Processing ${u.name}")
}
}
// Safe calls with extension functions
fun String?.orDefault(default: String): String {
return this ?: default
}
val result = nullName?.orDefault("Unknown")
// Safe calls in expressions
class Profile(val bio: String?)
fun displayBio(profile: Profile?) {
val bioLength = profile?.bio?.length ?: 0
println("Bio length: $bioLength")
}
// Safe calls with mutable properties
class Container {
var value: String? = null
fun updateValue() {
value?.let { current ->
value = current.uppercase()
}
}
}
```
Safe call chains short-circuit at the first null, making deeply nested optional
access clean and safe without multiple null checks.
## Elvis Operator and Null Coalescing
The Elvis operator `?:` provides default values for null expressions, enabling
concise fallback logic without verbose if-else statements.
```kotlin
// Basic Elvis operator
val name: String? = null
val displayName = name ?: "Guest"
println(displayName) // "Guest"
// Elvis with safe calls
fun getUserCity(employee: Employee?): String {
return employee?.company?.address?.city ?: "Unknown"
}
// Elvis with expressions
fun calculateTotal(subtotal: Double?, taxRate: Double?): Double {
val sub = subtotal ?: 0.0
val tax = taxRate ?: 0.15
return sub * (1 + tax)
}
// Elvis with return/throw
fun requireName(name: String?): String {
return name ?: throw IllegalArgumentException("Name required")
}
fun processUser(user: User?) {
val u = user ?: return
println("Processing ${u.name}")
}
// Chaining Elvis operators
fun findValidValue(
primary: String?,
secondary: String?,
tertiary: String?
): String {
return primary ?: secondary ?: tertiary ?: "default"
}
// Elvis with nullable properties
class Config {
var timeout: Int? = null
fun getTimeout(): Int {
return timeout ?: 30000
}
}
// Elvis in constructors
class Service(name: String?) {
val serviceName: String = name ?: "DefaultService"
}
// Elvis with function calls
fun fetchFromCache(): String? = null
fun fetchFromNetwork(): String? = "data"
fun getData(): String {
return fetchFromCache() ?: fetchFromNetwork() ?: "fallback"
}
```
The Elvis operator evaluates the right side only if the left side is null,
supporting lazy evaluation of default values.
## Smart Casts
Smart casts automatically cast nullable types to non-nullable after null checks,
eliminating redundant casts and improving code clarity.
```kotlin
// Smart cast after null check
fun printLength(text: String?) {
if (text != null) {
println(text.length) // text is smart-cast to String
}
}
// Smart cast in expressions
fun processName(name: String?): Int {
return if (name != null) {
name.length // Smart-cast
} else {
0
}
}
// Smart cast with return
fun requireUser(user: User?): User {
if (user == null) {
throw IllegalStateException("User required")
}
return user // Smart-cast to User
}
// Smart cast with Elvis
fun getLength(text: String?): Int {
val nonNull = text ?: return 0
return nonNull.length // Smart-cast
}
// Smart cast with when expressions
fun describe(obj: Any?): String {
return when {
obj == null -> "null"
obj is String -> "String of length ${obj.length}"
obj is Int -> "Int: $obj"
else -> "Unknown type"
}
}
// Smart cast with let
fun processNullable(value: String?) {
value?.let { nonNull ->
println(nonNull.uppercase()) // nonNull is String
}
}
// Smart cast limitations
class Container(var value: String?) {
fun process() {
// Cannot smart-cast var properties
if (value != null) {
// println(value.length) // Compilation error
}
// Use local variable for smart cast
val localValue = value
if (localValue != null) {
println(localValue.length) // OK
}
}
}
// Smart cast with contracts
fun requireNotNull(value: String?) {
require(value != null)
println(value.length) // Smart-cast after require
}
```
Smart casts work with immutable variables and val properties but not var
properties, which could change between the check and usage.
## Not-Null Assertion and Platform Types
The not-null assertion operator `!!` explicitly throws NPE if a value is null,
useful for cases where null is impossible but the compiler cannot verify.
```kotlin
// Not-null assertion operator
fun processName(name: String?) {
val length = name!!.length // Throws NPE if name is null
println("Length: $length")
}
// Use cases for !!
fun initializeFromConfig(config: Map<String, String>) {
// We know these keys exist
val apiKey = config["api_key"]!!
val endpoint = config["endpoint"]!!
println("Configured with $apiKey at $endpoint")
}
// Avoid chaining !!
val city = employee!!.company!Related in Data & Analytics
clawarr-suite
IncludedComprehensive management for self-hosted media stacks (Sonarr, Radarr, Lidarr, Readarr, Prowlarr, Bazarr, Overseerr, Plex, Tautulli, SABnzbd, Recyclarr, Unpackerr, Notifiarr, Maintainerr, Kometa, FlareSolverr). Deep library exploration, analytics, dashboard generation, content management, request handling, subtitle management, indexer control, download monitoring, quality profile sync, library cleanup automation, notification routing, collection/overlay management, and media tracker integration (Trakt, Letterboxd, Simkl).
querying-soql
IncludedSOQL query generation, optimization, and analysis with 100-point scoring. Use this skill when the user needs SOQL/SOSL authoring or optimization: natural-language-to-query generation, relationship queries, aggregates, query-plan analysis, and performance or safety improvements for Salesforce queries. TRIGGER when: user writes, optimizes, or debugs SOQL/SOSL queries, touches .soql files, or asks about relationship queries, aggregates, or query performance. DO NOT TRIGGER when: bulk data operations (use handling-sf-data), Apex DML logic (use generating-apex), or report/dashboard queries.
app-store-optimization
IncludedApp Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklists, and tracking ranking changes.
habit-flow
IncludedAI-powered atomic habit tracker with natural language logging, streak tracking, smart reminders, and coaching. Use for creating habits, logging completions naturally ("I meditated today"), viewing progress, and getting personalized coaching.
app-store-optimization
IncludedApp Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklists, and tracking ranking changes.
visualizing-data
IncludedBuilds dashboards, reports, and data-driven interfaces requiring charts, graphs, or visual analytics. Provides systematic framework for selecting appropriate visualizations based on data characteristics and analytical purpose. Includes 24+ visualization types organized by purpose (trends, comparisons, distributions, relationships, flows, hierarchies, geospatial), accessibility patterns (WCAG 2.1 AA compliance), colorblind-safe palettes, and performance optimization strategies. Use when creating visualizations, choosing chart types, displaying data graphically, or designing data interfaces.