Typy null

Jedna z najważniejszych cech Kotlina: język rozdziela typy, które nigdy nie są puste (String), od tych, które mogą być puste (String?). Dowiesz się, czym jest null i błąd NullPointerException oraz jak Kotlin chroni Cię przed nim już na etapie pisania kodu.

String String? null NPE
1

Czym jest null

null oznacza brak wartości – „nic”, „pustka”. To coś innego niż zero czy pusty tekst: zmienna o wartości null nie wskazuje na żadne dane. Pojawia się np. wtedy, gdy czegoś nie udało się znaleźć albo nie podano (pamiętasz toIntOrNull() z lekcji o wczytywaniu?).

WartośćZnaczenie
0liczba zero – konkretna wartość
""pusty tekst – istnieje, tylko nic nie zawiera
nullbrak jakiejkolwiek wartości
2

NullPointerException – groźny błąd

Problem zaczyna się, gdy program próbuje coś zrobić z wartością, której nie ma. Próba odwołania się do null (np. odczytania długości nieistniejącego tekstu) powoduje błąd NullPointerException, w skrócie NPE – jeden z najczęstszych powodów „wysypywania się” programów.

„Błąd wart miliard dolarów”

Pomysł wartości null ma już dziesięciolecia, a jego twórca sam nazwał go później swoim „błędem wartym miliard dolarów” – tyle kosztowały świat awarie spowodowane odwołaniami do nieistniejących wartości. Kotlin powstał m.in. po to, by ten problem rozwiązać.

Kluczowa idea Kotlina: zamiast pozwalać, by NPE wybuchł w czasie działania programu (u użytkownika!), język wyłapuje ryzyko już podczas pisania kodu.

3

Typy nienullowalne

Domyślnie typy w Kotlinie są nienullowalne – nie wolno przypisać im null. Zmienna typu String zawsze zawiera jakiś tekst, więc można jej bezpiecznie używać.

String zawsze ma wartość
val imie: String = "Anna"

// imie = null            // BŁĄD już przy pisaniu – String nie może być null

println(imie.length)        // bezpieczne – imie na pewno istnieje
println(imie.uppercase())   // bezpieczne
Gwarancja istnienia wartości

Gdy widzisz typ bez znaku zapytania (String, Int, Double…), masz pewność, że wartość istnieje. Nie musisz niczego sprawdzać – po prostu jej używasz.

4

Typy nullowalne – znak zapytania

Czasem brak wartości jest naturalny (np. nieznaleziony wynik). Aby pozwolić zmiennej być pustą, dopisujemy do typu znak zapytania: String?. Taki typ może zawierać tekst albo null.

String? może być null
val imie: String? = "Anna"     // może mieć tekst...
val drugie: String? = null     // ...albo być puste
TypDozwolone wartości
Stringtylko tekst (nigdy null)
String?tekst albo null
Inttylko liczba
Int?liczba albo null
Dodawaj ? tylko wtedy, gdy brak wartości ma sens

Znak zapytania to świadoma decyzja: „ta wartość naprawdę może nie istnieć”. Jeśli wartość zawsze istnieje, nie dodawaj ? – dzięki temu unikasz późniejszych komplikacji.

5

Jak Kotlin zapobiega NPE

Skoro String? może być null, Kotlin nie pozwoli użyć go wprost – bo groziłoby to NPE. Próba odczytania np. .length na wartości nullowalnej to błąd już podczas pisania:

Bezpośrednie użycie jest zablokowane
val imie: String? = pobierzImie()

// println(imie.length)   // BŁĄD! imie może być null

Jednym z najprostszych sposobów jest sprawdzenie, czy wartość nie jest null. Wewnątrz takiego warunku Kotlin wie, że wartość istnieje, i pozwala jej użyć:

Po sprawdzeniu można bezpiecznie użyć
val imie: String? = pobierzImie()

if (imie != null) {
    println(imie.length)   // OK – w tym miejscu imie na pewno nie jest null
} else {
    println("Brak imienia")
}
To dopiero początek

To tylko jeden ze sposobów. W następnej lekcji poznasz wygodne operatory ?., ?: i !!, a mechanizm „Kotlin wie, że tu nie ma null” (tzw. smart cast) rozłożymy na części osobno. Tu najważniejsze: nullowalnego nie da się użyć wprost – i to właśnie chroni przed NPE.

6

Przypisania między typami

Kierunek ma znaczenie. Wartość nienullowalną zawsze można włożyć tam, gdzie dozwolony jest null (bo „na pewno coś jest” spełnia warunek „coś albo nic”). Odwrotnie już nie – nullowalnej nie wolno przypisać do typu, który nie dopuszcza null.

Dozwolony i niedozwolony kierunek
val pewne: String = "Anna"
val moze: String? = pewne      // OK – nie-null pasuje do nullowalnego

val cosNull: String? = null
// val pewne2: String = cosNull    // BŁĄD! nullowalne nie pasuje do nienullowalnego
Łatwy sposób na zapamiętanie

„Pewne” mieści się w „może” – ale „może” nie mieści się w „pewne”. Aby przejść z String? do String, trzeba najpierw rozprawić się z możliwością null (o tym w kolejnych lekcjach).

7

Częste błędy

❌ Błąd 1: null w typie nienullowalnym

❌ String nie przyjmie null

val imie: String = null
// Błąd! String nie może
// być null

✅ Dodaj ?, jeśli null ma być możliwy

val imie: String? = null
// OK

❌ Błąd 2: bezpośrednie użycie nullowalnego

❌ Użycie bez sprawdzenia

val imie: String? = pobierz()
println(imie.length)
// Błąd! imie może być null

✅ Najpierw sprawdź

val imie: String? = pobierz()
if (imie != null) {
    println(imie.length)
}

❌ Błąd 3: nullowalne tam, gdzie wymagane nie-null

❌ Przypisanie w złą stronę

val moze: String? = "tekst"
val pewne: String = moze
// Błąd! moze mogłoby
// być null

✅ Typy zgodne

val moze: String? = "tekst"
val nadalMoze: String? = moze
// OK

❌ Błąd 4: zapomniany ? przy wyniku, który bywa null

❌ toIntOrNull zwraca Int?

val liczba: Int =
    readln().toIntOrNull()
// Błąd! wynik może
// być null

✅ Typ nullowalny Int?

val liczba: Int? =
    readln().toIntOrNull()
// OK
8

Podsumowanie

  • null oznacza brak wartości – to nie to samo co 0 czy pusty tekst.
  • Odwołanie do null powoduje błąd NullPointerException (NPE) w czasie działania programu.
  • Domyślnie typy są nienullowalne (String) – zawsze mają wartość, można ich bezpiecznie używać.
  • Dopisanie ? tworzy typ nullowalny (String?) – może zawierać wartość albo null.
  • Kotlin nie pozwala użyć nullowalnego wprost – wyłapuje ryzyko NPE już podczas pisania kodu.
  • Nie-null można przypisać do nullowalnego, ale nie odwrotnie (bez wcześniejszego sprawdzenia).
9

Zadania do wykonania

Zadania z wczytywaniem uruchamiaj w IntelliJ IDEA; pozostałe w Kotlin Playground.

Zadanie 1 łatwe

Zadeklaruj zmienną typu String z dowolnym tekstem oraz zmienną typu String? o wartości null. Wypisz obie.

Zadanie 2 łatwe

Spróbuj przypisać null do zmiennej typu String. Zaobserwuj komunikat błędu, a następnie popraw typ na String?.

Zadanie 3 łatwe

Zadeklaruj zmienną String? i spróbuj wypisać jej .length bezpośrednio. Zobacz błąd, a potem owiń odwołanie w sprawdzenie if (x != null) { ... }.

Zadanie 4 średnie

Wczytaj liczbę przez readln().toIntOrNull() (wynik typu Int?). Za pomocą if sprawdź, czy nie jest null: jeśli tak – wypisz „Nieprawidłowa liczba”, w przeciwnym razie wypisz jej kwadrat. Zwróć uwagę, że dopiero po sprawdzeniu Kotlin pozwala użyć wartości w obliczeniach.