1. Gioi thieu
Neu ban moi bat dau, compiler de bi cam nhan nhu mot giao vien cau ca, thay vi viet “sai roi” thi no viet: “Type mismatch: inferred type is String? but String was expected” — roi bien mat. Nhung thuc ra compiler Kotlin kha la “thang than”: no gan nhu luon noi ro ban dang co gang lam gi va vi sao dieu do nguy hiem. Muc tieu cua chung ta la hoc cach nhin ra, dang sau “cau noi dang so”, mot tinh huong rat doi thuong: “toi co the khong co gia tri, nhung toi lai cu xu nhu chac chan co”.
Tu hom nay, chung ta se xem cac thong diep ve nullability nhu nhung goi y, chu khong phai hinh phat. Trong IntelliJ IDEA (dac biet o che do K2) con de chiu hon: IDE to sang cho co van de va thuong de xuat cac cach sua nhanh.
Y tuong chinh: T? dang bi dung nhu T
Khi ban gap cac loi lien quan den null, se rat co ich neu giu trong dau mot mo hinh don gian: Kotlin chia gia tri thanh hai “dang cap”. O dang cap thu nhat la cac kieu thong thuong (String, Int) — chung cam ket: “gia tri ton tai”. O dang cap thu hai la nullable (String?, Int?) — chung canh bao thang than: “co the khong co gia tri”.
Neu ban co gang truy cap thanh vien cua doi tuong (.length, .uppercase()), truyen gia tri vao ham ma doi hoi non-null, hoac lam phep toan, compiler se hoi: “neu o do la null thi sao?”. Va chung nao ban chua tra loi cau hoi nay bang code, no se khong compile chuong trinh.
Một “luu do tu duy” nho:
flowchart TD
A[Da nhan duoc gia tri kieu T?] --> B{Tiep theo lam gi?}
B --> C[Kiem tra null va chon nhanh]
B --> D[Dung ?. de truy cap an toan]
B --> E[Dung ?: de chen gia tri mac dinh]
B --> F[Dung !! va chap nhan co the crash]
C --> G[Trong nhanh non-null: lam viec nhu voi T]
Day khong phai la “bon triet ly khac nhau”, ma la bon cau tra loi khac nhau cho mot cau hoi: ta xu ly viec thieu gia tri nhu the nao.
Bang “phien dich”: thong diep → nguyen nhan → cach sua thuong gap
Thong diep cua compiler thuong lam ban hoang so chi vi trong no giong “cong van hanh chinh”. Hay bien chung thanh mot bang “nguoi” hon. O day la nhung cau thong dung nhat ma ban se gap o giai doan nay.
| Thong diep compiler (thuong gap) | Y nghia theo cach noi doi thuong | Can nhin gi trong code | Thuong sua bang cach nao |
|---|---|---|---|
Type mismatch: inferred type is String? but String was expected |
Ban dua String? vao noi can String | Kieu ben trai/ben phai, kieu tham so cua ham | ?: (mac dinh), return som, kiem tra if (x == null) |
| Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver | Ban goi method/property tren T? nhu the no la T | Noi co dau cham: x.something | x?.something, hoac if (x != null) x.something |
| Operator call corresponds to a dot-qualified call ... which is not allowed on a nullable receiver | Tuong tu, nhung danh cho toan tu (+, *) | Phep toan tren Int?, Double? | Loai bo nullable: ?:, kiem tra null, dung “co the la so” de nhan/chia cong tru |
| Smart cast to 'String' is impossible, because ... | Compiler khong the dam bao gia tri khong bi thay doi | Thuong la var, property cua object, hoac “qua xa” so voi cho kiem tra | Chup snapshot vao val, dua viec su dung gan lai cho kiem tra, ?./?: |
| Unnecessary safe call on a non-null receiver | Ban viet ?. nhung ben trai da non-null roi | Kieu cua bien da la T | Bo ?. (khong phai loi, chi la don dep) |
Quan trong: hom nay chung ta khong “hoc thuoc van ban thong diep”. Chung ta hoc doan y dua tren cau truc: gan nhu luc nao cung co goi y nhu expected/found, nullable receiver, smart cast impossible.
2. Thuat toan sua: noi ban phai quyet dinh so phan cua null
Khi thay loi, phan xa thuong la lam dieu nhanh nhat: them !! va “ke no”. Day la phan xa rat con nguoi, khong can phan xet. Nhung hay thong nhat the nay: !! giong nhu dap cua bang chan. Doi khi cua that su bi khoa va ban la linh cuu hoa. Nhung thuong hon la ban de quen chia khoa o cai ao khac.
Thay vi vay, ta se theo mot thuat toan binh tinh.
Truoc het, ban tim nullable xuat hien chinh xac o dau. O giai doan nay cua khoa hoc, nguon pho bien nhat la toIntOrNull() va nhung bien ma ban tu ghi ro kieu String?/Int?. Sau do, ban tra loi cau hoi: ta muon lam gi neu o do la null? Ve co ban co vai lua chon: hien thong bao, dung gia tri mac dinh, yeu cau nhap lai, hoac dung kich ban mot cach an toan.
Sau do moi chon ky thuat: kiem tra if, safe call ?., Elvis ?:, hoac (hiem khi) !!.
De thuc te, hay cung lam mot mini-app, roi “nuoi lon” no bang cac vi du.
Mini-app: phieu thong tin nguoi dung va noi loi duoc sinh ra
Hinh dung mot chuong trinh don gian: hoi ten va tuoi, sau do in loi chao. Tren giay, moi thu rat de thuong. Trong code thuc te, nguoi dung co the nhap “hai muoi tam”, them khoang trang, hoac chi bam Enter vi… thich.
Bat dau voi phien ban thang thung — gan nhu chac chan se dan ban den cac thong diep compiler ma hom nay ta hoc cach “phien dich”.
fun main() {
print("Nhap ten: ")
val name = readln().trim()
print("Nhap tuoi: ")
val age = readln().trim().toIntOrNull()
println("Xin chao, $name! Ban $age tuoi.")
// Vi du output: Xin chao, Alice! Ban 28 tuoi.
}
Nhìn lan dau — on. Nhưng kieu cua age o day la Int?, va no bat dau “lan truyen” sang phan con lai: ban co the in ra “ban null tuoi”, va neu sau nay muon so sanh tuoi hay lam toan — compiler se bat dau phan doi.
Va day chinh la trong tam: thong diep cua compiler xuat hien khong phai vi “Kotlin kho tinh”, ma vi ban chua quyet dinh: lam gi khi nhap sai.
3. Loi #1: Type mismatch ... String? vs String
Ban se gap thong diep nay rat som, khi co gang tra ve gia tri nullable o noi ban da hua la non-null.
Vi du, ban muon viet ham “chuan hoa ten”:
fun normalizeName(raw: String?): String {
return raw?.trim()
}
No se khong compile, va ban se thay loi dai loai:
Type mismatch: inferred type is String? but String was expected
Tai sao? Vi raw?.trim() co kieu String?. Neu raw == null, ket qua cung la null. Trong khi trong signature ban da hua tra ve String (nghia la “chuoi chac chan ton tai”).
Cach sua phu thuoc vao y nghia. Cach don gian nhat (va thuong dung cho UI/output) la dua gia tri mac dinh bang Elvis:
fun normalizeName(raw: String?): String {
return raw?.trim() ?: "Anh hung vo danh"
}
fun main() {
println(normalizeName(" Alice ")) // Alice
println(normalizeName(null)) // Anh hung vo danh
}
Hay chu y: ban chat cua loi la compiler khong bat ban “phai them !!”. No bat ban quyet dinh ban muon lam gi khi raw khong ton tai.
Nguyen tac nay cung dung voi so:
fun parseAge(raw: String): Int {
return raw.trim().toIntOrNull()
}
O day se la: Type mismatch: inferred type is Int? but Int was expected.
Giai phap van la “y nghia”. Neu ban cho rang khi sai tuoi thi thay bang 0 (khong phai luc nao cung hop ly, nhung doi khi chap nhan duoc), thi:
fun parseAge(raw: String): Int {
return raw.trim().toIntOrNull() ?: 0
}
fun main() {
println(parseAge("42")) // 42
println(parseAge("oops")) // 0
}
Con neu 0 “che giu van de”, thi tot hon la tra ve Int? va bat code goi ham tu quyet dinh. Nhung do la mot hop dong (contract) khac — va compiler chinh la dang buoc ban chon hop dong mot cach co y thuc.
4. Loi #2: Only safe (?.) or non-null asserted (!!.) calls are allowed...
Thong diep nay thuong xuat hien khi ban lam kieu:
fun main() {
val nickname: String? = null
println(nickname.length)
}
null khong co do dai (no thuc chat khong phai object String), nen Kotlin khong cho ban viet code co the crash.
Giai ma loi: ban dang co gang goi thanh vien (length) tren gia tri co the khong ton tai. Cach sua lai phu thuoc vao y nghia.
Neu ban chi can “khong co thi thoi”, dung safe call:
fun main() {
val nickname: String? = null
val len: Int? = nickname?.length
println(len) // null
}
Neu ban can dam bao Int, them Elvis:
fun main() {
val nickname: String? = null
val len: Int = nickname?.length ?: 0
println(len) // 0
}
Neu ban muon mot logic khi co nickname va logic khac khi khong co — kiem tra if thuong de doc hon:
fun printNicknameInfo(nickname: String?) {
if (nickname == null) {
println("Khong co biet danh") // Khong co biet danh
return
}
println("Biet danh '$nickname', do dai = ${nickname.length}")
}
fun main() {
printNicknameInfo(null)
printNicknameInfo("neo")
}
O day quan trong la sau return compiler hieu: phan tiep theo nickname chac chan khong phai null. Do chinh la smart cast theo “phong cach tuyen tinh”.
5. Loi #3: so hoc nullable va toan tu
Doi khi ban khong viet dau cham, ma viet a + b, van nhan thong diep ve nullable receiver. Dieu nay co the lam ban ngac nhien: “toi co goi method dau!”. Nhung trong Kotlin, nhieu toan tu la “duong tat cu phap” (syntactic sugar) cho viec goi ham.
Vi du:
fun main() {
val a: Int? = "10".toIntOrNull()
val b: Int = 5
val sum = a + b
println(sum)
}
No se khong compile, vi a la Int?. Trong khi plus (thu nam sau +) ky vong ben trai la Int binh thuong.
Cach sua — van la “go nullable”. Cach thang nhat: dua gia tri mac dinh.
fun main() {
val a: Int = "10".toIntOrNull() ?: 0
val b: Int = 5
val sum = a + b
println(sum) // 15
}
Hoac, neu ban muon dung kich ban khi nhap sai:
fun main() {
val aOrNull = "oops".toIntOrNull()
if (aOrNull == null) {
println("Day khong phai so") // Day khong phai so
return
}
val sum = aOrNull + 5
println(sum)
}
Hay de y mau (pattern) thuong gap: val x = ... ?: return. No giup giu code “phang”, tranh nhieu if long nhau.
6. Loi #4: Smart cast ... is impossible
Day la thong diep “tuc nhat”, vi ban tuong nhu da lam dung: kiem tra null roi, ma compiler van bao “khong duoc”.
Vi du kinh dien — var:
fun main() {
var text: String? = "hi"
if (text != null) {
println(text.length) // doi khi ngay tai day compiler se bat dau tranh luan trong cac truong hop phuc tap hon
}
}
Trong truong hop don gian, compiler se cho phep. Nhung chi can phuc tap hon mot chut (vi du, thay doi text giua luc kiem tra va luc su dung) la no se mat “niem tin”:
fun main() {
var text: String? = "hi"
if (text != null) {
text = null
println(text.length) // luc nay khong duoc
println(text?.length) // duoc
}
}
Phien dich loi smart cast: compiler khong the dam bao gia tri van giong nhu luc kiem tra.
Mot cach don gian de “lay lai niem tin” la chup snapshot vao val (tham chieu bat bien) va lam viec voi no:
fun main() {
var text: String? = "hi"
val snapshot: String? = text
if (snapshot != null) {
println(snapshot.length) // 2
}
}
Meo nay nghe hoi buon cuoi (“sao phai copy bien sang bien?”), nhung ve y nghia thi rat manh: ban co dinh gia tri tai mot thoi diem va lam viec voi thu khong the bi gan lai.
7. Hoan thien phieu thong tin “cho ra dang”
Canh bao cua compiler: khong “chui”, ma la giup do
Loi (errors) se dung viec build. Canh bao (warnings) thi khong, nhung thuong goi y rang code cua ban co the sach va ro rang hon.
Mot canh bao rat dien hinh: ban da lam bien thanh non-null, nhung van quen tay viet ?..
fun main() {
val name: String = "Alice"
println(name?.length) // warning: unnecessary safe call
println(name.length) // 5
}
Canh bao nay khong phai “bat be”. No noi: “ban da o vung an toan, khong can doi mu bao hiem nua”.
Vi du khac — khi Elvis vo nghia:
fun main() {
val x: Int = 10
val y = x ?: 0 // cai nay thuc ra khong compile: x khong nullable
println(x) // 10
}
O day compiler khong cho dung ?: vi ben trai khong phai T?. Day cung la mot lop bao ve huu ich: Elvis la cong cu danh rieng cho nhanh nullable.
Phieu thong tin: kieu ro rang, nhanh xu ly ro rang
Gio hay quay lai phieu thong tin va lam no sao cho compiler thay “hai long”, con hanh vi khi loi thi du doan duoc.
Quy tac: ten phai khong rong, tuoi phai la so. Neu tuoi khong phai so — in thong bao va ket thuc chuong trinh.
fun main() {
print("Nhap ten: ")
val name = readln().trim()
if (name.isEmpty()) {
println("Ten khong duoc de trong") // Ten khong duoc de trong
return
}
print("Nhap tuoi: ")
val age = readln().trim().toIntOrNull() ?: run {
println("Tuoi phai la mot con so") // Tuoi phai la mot con so
return
}
println("Xin chao, $name! Ban $age tuoi.") // Vi du: Xin chao, Alice! Ban 28 tuoi.
}
Hay chu y hai diem.
Thu nhat, age o day tro thanh Int, khong phai Int?, vi ?: hoac tra ve so, hoac ket thuc chuong trinh. Nghia la ve sau, cac phep toan va so sanh se khong bi “nhiem” nullable.
Thu hai, chung ta da chon ro hanh vi khi loi. Va chinh dieu nay la dieu compiler luon “doi” ban lam, chi la no dien dat hoi hanh chinh.
Khi nao nen dung !! va vi sao compiler co gang can ban
Vi chung ta lien tuc noi “dung sua tat ca bang !!”, hay noi that: !! khong ton tai chi de lam dep. No dung khi ban co y thuc chon hop dong: “neu o day la null thi day la loi chuong trinh”.
Vi du, ban co mot bien noi bo nullable chi vi API tra ve nhu the, nhung theo logic cua kich ban thi den do bat buoc phai co du lieu. Khi do !! tro thanh “cho gac cong”: neu logic bi pha vo, chuong trinh se crash ngay.
Nhung trong cac bai tap console co nhap lieu tu nguoi dung, !! gan nhu luon la y tuong te: nguoi dung khong co nghia vu phai tuan theo hop dong cua ban, ho chi don gian la… song.
Mini-vi du (no trong ra sao va vi sao rui ro):
fun main() {
val ageText = readln().trim()
val age = ageText.toIntOrNull()!!
println("age = $age")
}
No compile, nhung neu nhap khong phai so — chuong trinh se ket thuc dot ngot. Compiler da canh bao, con ban thi bo qua. It nhat, chi lam vay khi crash thuc su la hanh vi ban mong muon.
8. Loi thuong gap
Loi #1: “sua de compile”, thay vi sua logic.
Thuong thi nguoi moi thay to mau do, them ?: 0 hoac !!, va vui vi “chay duoc”. Nhung ve y nghia, chuong trinh bat dau noi doi: tuoi thanh 0, ten thanh “anonymous”, va nguoi dung nhan ket qua ky quac ma khong co giai thich. Cach dung la: dau tien quyet dinh se lam gi khi null, sau do moi chon toan tu.
Loi #2: nham lan null voi “gia tri rong”.
null la “khong co gia tri”, con "" la “co chuoi nhung rong”. Neu ban quy dinh ten khong duoc rong, do khong phai ly do de dung String?. Do la ly do de kiem tra isEmpty()/isBlank() va in thong bao. Nullable khong thay the validate.
Loi #3: “trai” xu ly null khap noi trong code.
Neu ban lay Int? tu toIntOrNull(), roi truyen qua nam ham, ban se co nam cho compiler “nhac nho” ve nullable. De hon nhieu la xu ly null gan noi no xuat hien: chuyen Int? thanh Int bang ?: hoac dung kich ban. Khi do phan con lai se don gian hon.
Loi #4: tin rang smart cast se “maii mai”.
Kiem tra if (x != null) chi lam x an toan trong mot pham vi ro rang, va chi khi compiler chac rang x khong bi thay doi. Neu x la var, neu ban di qua xa khoi cho kiem tra, hoac neu gia tri co the thay doi, smart cast se hu. Khi do, hoac dung ?./?:, hoac chup “snapshot” vao val.
Loi #5: dung ?. nhu the no “chen gia tri mac dinh”.
?. khong chen gi ca. No chi tra ve null neu ben trai la null. Neu ban can mac dinh (vi du do dai 0), do la viec cua ?:. Nhầm lẫn hai vai tro nay se dan den cac chuoi kieu val x = text?.length va null bat ngo o cac buoc tiep theo.
GO TO FULL VERSION