Explain sound null safety in Dart. How does it differ from Kotlin's null safety?
Dart sound null safety (since Dart 2.12 / Flutter 2.0): types are non-nullable by default; ? opts in to nullability; the compiler proves no null dereferences at compile time.
Key operators:
?nullable type,!force-unwrap,?.null-safe access,??null-coalesce,??=assign-if-null,latedeferred init,requiredfor named params.
Dart vs Kotlin:
Both sound (compiler-proven safety).
Dart's
lateworks onfinalfields; Kotlin'slateinitdoesn't.Dart has an explicit
requiredkeyword for named parameters; Kotlin uses non-null defaults + optional params.Force-unwrap:
!in Dart,!!in Kotlin.Null-coalesce:
??in Dart,?:'Elvis' operator in Kotlin.
Opting out of null safety is no longer supported in modern Flutter.
Dart Null Safety (sound, since Dart 2.12 / Flutter 2.0):
• Types are NON-NULLABLE by default
• `?` makes a type nullable
• Static analyzer + runtime guarantee no null dereference
int x = 1; // non-nullable int — must always have a value
int? y; // nullable int — can be null
String name = 'Alex';
// name = null; // ❌ compile error
// 'late' = non-nullable but initialized later
late final User user; // promise: I'll init before use
void init() => user = User.fetch();
// 'required' for named parameters
class Profile {
Profile({required this.name, this.email});
final String name;
final String? email;
}
// Null-aware operators:
final len = name?.length; // null-safe access
final value = name ?? 'default'; // null-coalescing
name ??= 'default'; // assign-if-null
final first = list?.first ?? 0; // chained
// 'sound' = the compiler PROVES no null dereferences
// (you can't lie to the type system with a runtime cast)
// =================================================
// Dart vs Kotlin null safety:
// =================================================
//
// Dart Kotlin
// ───── ──────
// String? nullable String? nullable
// String non-nullable String non-nullable
// ! force-unwrap !! force-unwrap
// ?. null-safe access ?. null-safe access
// ?? null-coalesce ?: 'Elvis' null-coalesce
// late deferred init lateinit deferred init (different rules)
// required keyword for params no equivalent — Kotlin has
// non-null default + nullable suffix
//
// Differences:
// • Dart's `late` lets you defer initialization of FINAL variables.
// Kotlin's `lateinit` only works on non-final, non-primitive vars.
// • Dart distinguishes 'required' for NAMED parameters explicitly.
// • Both are 'sound' — the type system PROVES safety at compile time.The code progresses from basic non-nullable/nullable declarations to the four core null-aware operators (?., ??, ??=, !) and then to deferred initialization (late final) and required named parameters. The side-by-side table compares Dart's operators to Kotlin's so candidates coming from JVM mobile development can map equivalents quickly.
Interview tip: The key takeaway is that the static analyzer enforces these rules at compile time — runtime null dereferences become impossible without an explicit
!cast.
Two terms to use precisely: 'sound null safety' (the compiler proves no null dereferences — Dart 2.12+) and non-nullable by default. The Kotlin vs Dart comparison interviewers like: (1) late in Dart works on final fields, Kotlin's lateinit doesn't; (2) Dart has required for named parameters explicitly — Kotlin handles it via non-null defaults. End with the practical signal: 'in modern Flutter (since 2.0/Dart 2.12), opting out of null safety is no longer supported'.