Lambda: nó là gì, cú pháp và cách sử dụng trong Kotlin

Tác giả: IT Sectr Đã đăng: 2026-06-23 Thời gian đọc: 7 phút

Lambda là một hàm ẩn danh được biểu diễn dưới dạng biểu thức nhỏ gọn { đối_số -> thân }. Trong Kotlin, lambda là cách chính để truyền hành vi vào các hàm bậc cao, cung cấp cú pháp ngắn gọn mà không cần khai báo một hàm riêng biệt. Theo tài liệu Kotlin (2026), hơn 70% các hàm bậc cao trong các dự án điển hình được gọi với lambda. Lambda cho phép viết mã theo phong cách hàm với tối thiểu nhiễu cú pháp.

Những điểm chính

  • Lambda — hàm ẩn danh với cú pháp { args -> body }, không có tên
  • it ngầm định thay thế đối số duy nhất của lambda, đơn giản hóa cách viết
  • Trailing lambda — đưa lambda cuối ra ngoài dấu ngoặc gọi để dễ đọc
  • Bao đóng (closure) — lambda có thể truy cập biến từ phạm vi bên ngoài
  • Kiểu hàm (T) -> R định nghĩa chữ ký của lambda và được trình biên dịch kiểm tra

Lambda là gì?

Lambda (biểu thức lambda) là một hàm ẩn danh được định nghĩa như một biểu thức thay vì một khai báo. Thuật ngữ này bắt nguồn từ phép tính lambda của Alonzo Church (1936). Trong lập trình, lambda là một khối mã có thể được truyền như một giá trị, lưu trữ trong biến và gọi sau đó. Trong Kotlin, lambda luôn được đặt trong dấu ngoặc nhọn { }, với các đối số được phân cách khỏi thân bằng mũi tên ->.

Lambda khác với hàm thông thường ở chỗ không có tên và từ khóa fun. Điều này cho phép khai báo hành vi trực tiếp tại điểm sử dụng mà không làm lộn xộn mã với các khai báo bổ sung. Trình biên dịch Kotlin suy luận kiểu đối số từ ngữ cảnh, vì vậy trong nhiều trường hợp có thể bỏ qua kiểu. Lambda là cú pháp đường bao quanh kiểu hàm (T) -> R: mọi lambda đều có một kiểu hàm tương ứng.

Công dụng chính của lambda trong Kotlin là truyền chúng vào các hàm bậc cao. Thư viện chuẩn sử dụng lambda ở khắp mọi nơi: filter { it > 0 }, map { it.uppercase() }, forEach { println(it) }. Lambda cũng được sử dụng cho bộ lắng nghe sự kiện, callback và bộ cấu hình đối tượng thông qua apply và run, làm cho mã khai báo hơn so với các lớp ẩn danh.

Cú pháp biểu thức lambda trong Kotlin

Dạng đầy đủ của một biểu thức lambda trong Kotlin: { đối_số -> thân_lambda }. Các đối số được liệt kê cách nhau bằng dấu phẩy, tiếp theo là mũi tên và thân — một hoặc nhiều biểu thức. Nếu thân chứa nhiều dòng, biểu thức cuối cùng trở thành giá trị trả về. Nếu một hàm nhận lambda làm đối số cuối cùng, nó có thể được đưa ra ngoài dấu ngoặc gọi.

kotlin
val sum: (Int, Int) -> Int = { a, b -> a + b }

// Kiểu được suy luận từ ngữ cảnh
val sumShort = { a: Int, b: Int -> a + b }

// Gọi Lambda
val result = sum(5, 3)  // 8
println(result)

Biến sum được khai báo với kiểu hàm tường minh (Int, Int) -> Int và được khởi tạo với lambda { a, b -> a + b }. Đối với sumShort, kiểu được chỉ định bên trong lambda và kiểu biến được suy luận. Cả hai biến thể đều tương đương. Gọi một lambda không khác gì gọi một hàm thông thường — sử dụng cú pháp sum(5, 3). Lambda không có đối số được viết là { body }, với một đối số — { it -> it * 2 } hoặc đơn giản { it * 2 }.

it ngầm định và lược bỏ đối số

Khi lambda nhận chính xác một đối số, Kotlin cung cấp tên ngầm định it. Điều này loại bỏ nhu cầu khai báo đối số ở phía bên trái. Nhà phát triển chỉ viết thân lambda, sử dụng it để truy cập tham số duy nhất. Cú pháp này đặc biệt thuận tiện trong chuỗi xử lý bộ sưu tập, nơi it đề cập đến phần tử hiện tại.

kotlin
val numbers = listOf(1, 2, 3, 4, 5)

// Tên tham số tường minh
val squaredExplicit = numbers.map { n -> n * n }

// it ngầm định
val squared = numbers.map { it * it }

// Lọc với it
val even = numbers.filter { it % 2 == 0 }

println(squared) // [1, 4, 9, 16, 25]

Trong ví dụ, map { it * it } sử dụng it thay vì khai báo n ->. Kotlin tự động gán it cho một tham số lambda duy nhất. Nếu lambda nhận hai hoặc nhiều đối số, it không được tạo — tất cả tham số phải được khai báo tường minh. Để cải thiện khả năng đọc của các lambda lồng nhau, nên đặt tên có ý nghĩa cho các tham số và chỉ sử dụng it trong các vị từ ngắn một dòng.

Trailing lambda và lambda nhiều dòng

Trailing lambda là một tính năng cú pháp của Kotlin cho phép đưa đối số lambda cuối cùng ra ngoài dấu ngoặc gọi hàm. Điều này làm cho mã trông giống như các cấu trúc ngôn ngữ tích hợp sẵn. Nếu lambda là đối số duy nhất, dấu ngoặc có thể được bỏ qua hoàn toàn. Cách tiếp cận này được sử dụng tích cực trong thư viện chuẩn và DSL.

kotlin
// Gọi chuẩn
thread(block = {
    println("Xin chào từ luồng")
})

// Phiên bản trailing lambda
thread {
    println("Xin chào từ luồng")
}

// lặp với trailing lambda
repeat(3) { index ->
    println("Lặp $index")
}

// chạy với lambda nhiều dòng
val config = run {
    val host = "localhost"
    val port = 8080
    "$host:$port"
}

Hàm thread nhận Runnable làm đối số cuối cùng. Nhờ trailing lambda, lambda có thể được truyền bên ngoài dấu ngoặc, trông giống như một khối mã. Hàm repeat nhận số lần lặp và một lambda với chỉ mục. run là một ví dụ về hàm bậc cao nơi lambda nhiều dòng tính toán một giá trị được trả về bởi biểu thức cuối cùng. Dấu ngoặc quanh run() có thể được bỏ qua nếu lambda là đối số duy nhất.

Chụp biến (closure) trong lambda

Chụp biến (closure) là khả năng của lambda để truy cập các biến được khai báo trong phạm vi bên ngoài. Trong Kotlin, lambda có thể đọc và sửa đổi biến var, cũng như đọc biến val từ ngữ cảnh xung quanh. Các biến được chụp được lưu trữ cùng với lambda trong suốt vòng đời của nó, cho phép tạo các hàm có trạng thái.

kotlin
fun makeCounter(): () -> Int {
    var count = 0
    return {
        count++  // số lần chụp
    }
}

fun main() {
    val counter = makeCounter()
    println(counter()) // 0
    println(counter()) // 1
    println(counter()) // 2
}

Hàm makeCounter khai báo một biến cục bộ count và trả về một lambda { count++ }. Mặc dù makeCounter đã hoàn thành thực thi, lambda được trả về vẫn giữ tham chiếu đến count thông qua bao đóng. Mỗi lần gọi counter() đều tăng và trả về giá trị hiện tại. Cơ chế closure là nền tảng của currying, nhà máy hàm và ghi nhớ. Điều quan trọng cần nhớ là việc chụp các biến có thể thay đổi có thể dẫn đến hành vi không mong muốn trong thực thi đồng thời.

Câu hỏi thường gặp

Lambda có thể có return không?

Lambda không hỗ trợ return vô điều kiện — return không có nhãn sẽ trả quyền điều khiển cho hàm bên ngoài. Để thoát khỏi lambda, hãy sử dụng return@label, trong đó label là tên của hàm bậc cao mà lambda đã được truyền vào.

Sự khác biệt giữa lambda và hàm ẩn danh là gì?

Hàm ẩn danh được viết là fun(a: Int): Int { return a * 2 } và hỗ trợ return không có nhãn. Lambda { a -> a * 2 } ngắn hơn về cú pháp nhưng yêu cầu return@label để thoát sớm.

Làm thế nào để truyền lambda làm đối số mà không có mũi tên?

Nếu lambda không nhận đối số, mũi tên được bỏ qua: { body() }. Nếu nó nhận một đối số nhưng không sử dụng, hãy viết { _ -> body() } với dấu gạch dưới để bỏ qua tham số.

Lambda có luôn tạo một đối tượng mới không?

Có, mọi lambda được truyền vào hàm không inline đều tạo một thể hiện của một lớp ẩn danh. Đối với hàm inline, lambda được nội tuyến tại vị trí gọi mà không tạo đối tượng. Các lambda không chụp được lưu vào bộ nhớ đệm dưới dạng đối tượng đơn thể.

Tại sao không thể sử dụng return trực tiếp trong lambda?

Return trong Kotlin là một toán tử nhảy thuộc về hàm gần nhất được khai báo với fun. Lambda không phải là một hàm (fun), mà là một biểu thức. Do đó, return bên trong lambda không có nhãn được coi là trả về từ hàm bên ngoài bao quanh.

Tổng kết

  • Lambda — hàm ẩn danh với cú pháp { args -> body }, được truyền như giá trị vào hàm bậc cao
  • it ngầm định thay thế đối số duy nhất, làm cho mã nhỏ gọn hơn trong các vị từ ngắn
  • Trailing lambda đưa lambda cuối ra ngoài dấu ngoặc gọi, đọc như một khối mã
  • Bao đóng (closure) cho phép lambda truy cập biến từ ngữ cảnh bên ngoài sau khi hàm chứa hoàn thành
  • Kiểu hàm (T) -> R định nghĩa chữ ký lambda với kiểm tra tại thời điểm biên dịch
  • Hàm inline loại bỏ việc tạo lớp ẩn danh cho lambda, cải thiện hiệu suất trong vòng lặp
  • Lambda nhiều dòng trả về giá trị của biểu thức cuối cùng, hỗ trợ tính toán phức tạp mà không cần khai báo hàm riêng

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm