Tìm hiểu về null safety trong kotlin

I, Nullable type

  • Một trong những vấn đề hay gặp phải nhất với các programming language (bao gồm cả java) là sử dụng 1 null variable (reference) sẽ gây ra null reference exception.
  • Trong java, bạn sẽ gặp phải NullPointerException hay NPE.
  • Kotlin cung cấp các công cụ để giúp bạn loại bỏ vấn đề trên trong code.
  • Do đó kotlin sẽ gặp phải NPE ở 1 trong các trường hợp sau:
    • 1, Bạn trực tiếp gọi throw NullPointerException().
    • 2, Sử dụng toán tử !! trên 1 null reference (mình sẽ nói sau).
    • 3, Gặp vấn đề khi sử dụng platform type hoặc do java code. Ví dụ: add null element vào MutableList<String?>.
    • 4, Liên quan tới việc khởi tạo của 1 object.
  • Kotlin giúp chúng ta phân biệt nullable reference và non-nullable reference.
  • Ví dụ 1:
1
2
3
var a: String = "abc" // Regular initialization means non-null by default
a = null // compilation error
val l = a.length // ok because it is safe because a isn't nullable
  • Ví dụ 2:
1
2
3
var b: String? = "abc" // can be set null
b = null // ok
val l = b.length // compilation error because b is nullable

II, Check điều kiện null

  • Cách xử lý thông thường giống với java là chúng ta sẽ check xem b có null hay không.
  • Ví dụ 3: nếu b khác null thì gán l = b.length không thì l = -1
1
val l: Int = if (b != null) b.length else -1

III, Safe call

  • Kotlin cung cấp 1 công cụ khác là safe call, nó thêm ?. vào sau variable.
  • Ví dụ 4:
1
2
3
4
5
val a = "Kotlin"
val b: String? = null
val l: Int? = b.length // b is nullable so l is also nullable.
println(b?.length) // need safe call because b is nullable
println(a?.length) // Unnecessary safe call because a isn't nullable
  • Safe call chain có dạng là v1?.v2?.v3[?.v4[?..]]. Nó rất hữu ích vì nó sẽ return null nếu bất cứ variable nào bị null.
  • Ví dụ 5: Nếu parent, parent.child1 hay parent.child1.child2 return null, safe call chain cũng sẽ return null
1
parent?.child1?.child2?.child3
  • Trong phép gán =, safe call có thể được đặt bên trái của =. Nếu 1 variable trong safe call chain return null, việc gán sẽ bị bỏ qua và biểu thức bên phải = sẽ không được gọi.
  • Ví dụ 6: Nếu person hay person.department trả về null, managersPool.getManager() sẽ không được gọi, do đó việc gán bị bỏ qua
1
person?.department?.head = managersPool.getManager()

IV, Toán tử Elvis

  • Kotlin cũng cung cấp 1 toán tử khác đó là evils, nó được viết là ?:.
  • Nếu expression bên trái của evils khác null, kết quả của evils là expression bên trái đó. Nếu không, kết quả của evils là expression bên phải.
  • Ví dụ 7: Viết lại ví dụ 3 bằng toán tử evils
1
val l = b?.length ?: -1
  • Trong kotlin, returnthrown cũng là các expression, do đó chúng có thể được dùng trong expression bên phải của evils.
  • Ví dụ 8:
1
2
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")

V, Toán tử !!

  • Toán tử !! không được khuyến khích vì nó xác nhận rằng variable không thể bị null (mặc dù variable vẫn có thể bị null) và sẽ throw NPE khi variable bị null.
  • Ví dụ 9: nếu b có giá trị null thì code sẽ throw NPE
1
val l = b!!.length

VI, Safe cast

  • Toán tử cast có thể gây ra ClassCastException nếu kiểu dữ liệu của variable không thuộc kiểu của class được cast.
  • Kotlin cung cấp safe cast, nó được viết là as?.
  • Safe cast sẽ trả về null nếu việc cast không thành công.
  • Ví dụ 10: nêu cast không thành công thì i sẽ có giá trị null
1
val i: Int? = a as? Int

V, Null type trong các collection

  • Nếu bạn sử dụng collection cho phép các nullable element, bạn có thể lọc để chọn ra những toán tử khác null bằng filterNotNull().
  • Ví dụ 11: kết quả của nonNullNumbers[1, 2, 4]
1
2
3
val numbers: List<Int?> = listOf(1, 2, null, 4)
val nonNullNumbers = numbers.filterNotNull()
println(nonNullNumbers)