Tìm hiểu về labels và jump expression trong Kotlin

I, Label

  • Trong Kotlin, bất cứ expression nào đều có thể được đánh dấu bằng label.
  • Label được tạo nên từ định danh của label và @ như abc@, lit@, forTest@
  • Để đặt label lên 1 expression, chúng ta chỉ cần đặt label trước expression đó.
  • Ví dụ 1: đặt loop@ label cho vòng lặp for
1
2
3
loop@ for (i in 1..100) {
// ...
}

II, Jump expression

  • Kotlin cung cấp 3 jump expression:
    • return: mặc định nó sẽ kết thúc function gần nhất chứa nó.
    • break: kết thúc vòng lập gần nhất.
    • continue: thực hiện bước tiếp theo của vòng lặp gần nhất.

1, Break và continue

  • Ví dụ 2: khi i = 3, vòng lặp for có label loop@ sẽ kết thúc.
1
2
3
4
5
6
loop@ for (i in 1..5) {
for (j in 1..3) {
if (i == 3) break@loop
print(j) // Print result: 123123
}
}
  • break kết hợp với label: kết thúc vòng lặp có label đó.
  • continue kết hợp với label: nhảy đến bước tiếp theo của vòng lặp có label đó.

2, return trong lambada

  • Mặc định, return sẽ return function gần nhất chứa nó.
  • Một tác dụng quan trọng khác là dùng return để return lambada expression.
  • Lambada có label sẽ trùng với tên của function chứa nó.
  • Trong lambada, Kotlin cho phép:
    • return có label: kết thúc lambada đó.
    • return không có label: nếu lambada ở trong inline function thì nó sẽ kết thúc function ngoài gần nó nhất.
  • Tham khảo bài viết về inline function để hiểu rõ hơn vấn đề này.
  • Ví dụ 4: return không có label. Do forEach() là inline function nên return kết thúc hàm gần nó nhất là foo()
1
2
3
4
5
6
7
8
9
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")

// Print result: 12
}
  • Ví dụ 5: return có label. Chúng ta có thể đặt lit@ cho lambada expression hoặc kotlin cũng đã sinh ra sẵn forEach@.
1
2
3
4
5
6
7
8
9
10
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
// if (it == 3) return@forEach
print(it)
}
print(" done with explicit label")
}

// Print result: 1245 done with implicit label

Ví dụ 6: lambada có label trùng với tên với function chứa chứa nó

1
2
3
4
5
6
7
8
9
10
fun makeLabel(action: () -> Unit){
action()
}

fun run(){
makeLabel {
print("Label")
return@makeLabel
}
}
  • Chúng ta không thể dùng breakcontinue trong lambada do chúng chỉ được dùng trong vòng lặp.

Ví dụ 7: chúng ta dừng việc lặp của list khi item nó bằng 3 và tiếp tục code của foo()

1
2
3
4
5
6
7
8
9
10
11
fun foo() {
run loop@{
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@loop // non-local return from the lambda passed to run
print(it)
}
}
print(" done with nested loop")
}

// Result: 12 done with nested loop