This episode dissects Kotlin's null safety and type system: nullable types and the safe call operator, the elvis operator and non-null assertions, smart casts and type inference, all the way to inline classes and value classes that keep your code safe while preserving optimal performance.

Null safety is the main reason many teams leave Java and move to Kotlin. Episode 6 dissects the type system that makes most NullPointerExceptions fail at compile time rather than in production. You'll learn nullable types, the safe call operator, the elvis operator, and smart casts.
Beyond null safety, this episode also touches on type inference and inline classes — a feature that lets you create new types without the overhead of object allocation.
After this episode, you'll understand why Kotlin is called a "null-safe" language and how to use it to write code with far fewer bugs.
In Kotlin, a String type can never hold null. If a variable can potentially be null, its type must be written as String?. The compiler enforces null handling at the point of use:
val nama: String? = null
val panjang = nama.lengthThe second line above will not compile — the compiler knows nama can be null and rejects direct access. This is what null safety means: problems are detected at build time, not when the app crashes in the user's hands.
To safely access a member of a nullable value, use the ?. operator. If the receiver is null, the whole expression returns null without throwing an exception:
val nama: String? = null
val panjang = nama?.length
println(panjang) // null, bukan crashnama?.length produces Int? — null if nama is null, the string length otherwise. A safe call chain like pengguna?.alamat?.kota eliminates the nested if-null blocks commonly written in Java.
The elvis operator ?: returns the value on the right side when the expression on the left is null. It replaces the if (x != null) x else default block:
val nama: String? = null
val tampil = nama ?: "anonim"
println(tampil) // anonimnama ?: "anonim" means: if nama is not null, use its value; if null, use "anonim". Elvis is often combined with return and throw to stop the flow when data is missing, such as val h = tinggi ?: return 0.
The !! operator forces a nullable value to non-null and throws a NullPointerException if the value turns out to be null. This is called "on your own responsibility":
val nilai: String? = cariNilai()
val pastiAda = nilai!!Use !! only when you are certain the value cannot be null — for example, after prior validation. In production code, !! is often considered a code smell: it's better to use a safe call or the elvis operator so unexpected null behavior is caught clearly.
Kotlin performs smart casts: if the compiler can prove a value is not null (or is already of a certain type) within a scope, it automatically casts it without extra syntax:
fun hitungPanjang(teks: String?): Int {
if (teks == null) return 0
return teks.length // teks sudah non-null di sini
}After the teks == null check that returns 0, the compiler knows teks is definitely non-null — so teks.length is called without a safe call. Smart casts also work for types: after is String, the value is treated as a String automatically.
Type inference lets you write without explicit types, but the compiler still checks all operations. Inferred types can sometimes turn out to be nullable without you realizing it — for example, from a function that returns String?. Get used to checking inferred types with your IDE to avoid surprises.
A value class (formerly an inline class) lets you create a semantically safe wrapper type without allocating an object at runtime — the value is wrapped directly in its underlying type:
@JvmInline
value class Usia(val nilai: Int)
@JvmInline
value class Nama(val nilai: String)
fun sapa(nama: Nama, usia: Usia): String {
return "Halo ${nama.nilai}, umur ${usia.nilai}"
}Usia and Nama are distinct types at the compile level — you can't mistakenly pass an Usia to a Nama parameter — yet at runtime both are represented as Int and String without a wrapper object. This feature provides type safety with no performance cost.
Use a value class to wrap primitives that carry different domain meanings, such as Usia, Harga, ID, or Email. The benefit is twofold: code is more expressive, and argument-swap bugs are caught at compile time, with no object-allocation penalty.
Some habits that keep your code safe:
?. for chained access that may be null.?: for default values instead of !!.!! to points that are truly guaranteed to be non-null.== null or is Tipe check.Episode 6 equips you with Kotlin's most valuable defense system: nullable types with safe calls, the elvis operator for default values, automatic smart casts, and value classes that combine type safety with runtime performance.
The key takeaways:
?. calls members safely; the expression yields null when the receiver is null.?: provides a default value for the null case.!! forces non-null and can throw an exception — use it sparingly.In episode 7 we'll discuss functional programming and lambdas — lambdas and anonymous functions, function references, scope functions like let, run, apply, also, and with, immutability patterns, and the functional idioms that make data transformation expressive.