Extension function trong Kotlin: nó là gì, cú pháp và ứng dụng

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

extension function — một cơ chế của Kotlin cho phép thêm các phương thức mới vào các lớp hiện có mà không cần kế thừa và không cần sửa đổi mã nguồn. Hàm được khai báo với tiền tố dưới dạng kiểu tiếp nhận và được gọi như một phương thức thông thường của kiểu đó. Theo Kotlin Documentation, 2025, extension functions được biên dịch thành các phương thức tĩnh với tham số đầu tiên là bộ tiếp nhận, đảm bảo không có overhead trong thời gian chạy so với các lệnh gọi thông thường.

Những điểm chính

  • Extension function — một phương thức với kiểu tiếp nhận, được gọi như thành viên của lớp mà không cần kế thừa
  • Kiểu tiếp nhận được chỉ định qua dấu chấm trước tên hàm: fun Type.method()
  • Phân phối tĩnh — extension function không ảo, được gọi dựa trên kiểu của biến
  • Import bắt buộc: extension functions không phải là thành viên của lớp và cần được import
  • Thành viên của lớp có ưu tiên: nếu phương thức trùng khớp, thành viên lớp được gọi, không phải extension

Extension function trong Kotlin là gì?

Extension function là một hàm được khai báo bên ngoài một lớp nhưng được gọi như phương thức của nó. Nó nhận một kiểu tiếp nhận mà phương thức mới sẽ được thêm vào. Bên trong hàm, từ khóa this tham chiếu đến thể hiện của kiểu tiếp nhận. Cách tiếp cận này cho phép mở rộng các lớp final, lớp thư viện bên thứ ba và thậm chí cả kiểu nguyên thủy mà không cần tạo wrapper.

Không giống như Java, nơi để thêm một phương thức vào lớp hiện có cần kế thừa hoặc một lớp tiện ích tĩnh, Kotlin cung cấp một giải pháp thanh lịch không có boilerplate. Theo một nghiên cứu của Kotlin Foundation (2024), extension functions nằm trong số năm tính năng được sử dụng nhiều nhất của ngôn ngữ — 78% nhà phát triển Kotlin sử dụng chúng trong công việc hàng ngày.

Trình biên dịch Kotlin dịch extension function thành một phương thức tĩnh, nơi thể hiện của bộ tiếp nhận được truyền như đối số đầu tiên. Điều này có nghĩa là extension functions không sửa đổi bytecode của lớp mục tiêu và không phá vỡ tính đóng gói — chỉ có các trường và phương thức công khai của bộ tiếp nhận mới có thể truy cập được.

Sử dụng extension functions để viết các hàm tiện ích sạch sẽ, gắn kết logic với một kiểu cụ thể nhưng không thể thêm vào mã nguồn của nó.

Cú pháp extension function: khai báo và gọi

Cú pháp cơ bản của extension function: tên kiểu tiếp nhận, dấu chấm, tên hàm, sau đó là tham số và kiểu trả về. Bên trong hàm, bộ tiếp nhận có thể truy cập qua this.

kotlin
// Extension function for String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// Called as regular String method
val result = "user@example.com".isEmail() // true

Trong ví dụ này, isEmail() trở nên khả dụng cho tất cả chuỗi. Bên trong hàm, this tham chiếu đến chính chuỗi mà phương thức được gọi trên đó. Kotlin cho phép bỏ qua this trong hầu hết các trường hợp — bạn có thể chỉ cần viết contains("@") thay vì this.contains("@").

Extension function với tham số

Extension functions có thể chấp nhận các tham số bổ sung và trả về giá trị của bất kỳ kiểu nào. Điều này làm cho chúng trở thành các hàm chính thức, không chỉ là đường cú pháp.

kotlin
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
    if (this.isEmpty()) return listOf(default)
    return this
}

val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)

Tham số default xác định giá trị trả về cho danh sách trống. Extension function có thể là generic — kiểu List là generic, mặc dù ví dụ sử dụng List cụ thể.

Phân phối tĩnh và bộ tiếp nhận

Extension functions trong Kotlin sử dụng phân phối tĩnh, không phải phân phối ảo. Đây là điểm khác biệt chính so với các phương thức lớp thông thường. Extension function nào được gọi được xác định tại thời điểm biên dịch bởi kiểu tĩnh của biến, không phải bởi kiểu thực tế trong thời gian chạy.

kotlin
open class Animal
class Dog : Animal()

fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"

fun test() {
    val animal: Animal = Dog()
    println(animal.speak()) // "Animal sound" — static type Animal

    val dog: Dog = Dog()
    println(dog.speak()) // "Woof" — static type Dog
}

Mặc dù biến animal trỏ đến một thể hiện của Dog, extension function cho Animal được gọi vì kiểu tĩnh của biến là Animal. Nếu speak() là một phương thức ảo của lớp, Dog.speak() sẽ được gọi. Hành vi này cần được xem xét khi thiết kế API với extension functions.

Import và phạm vi của extension functions

Extension functions không phải là thành viên của lớp — chúng là các hàm thông thường cần import để sử dụng trong tệp khác. Kotlin cung cấp hai tùy chọn import: theo tên hàm hoặc với đổi tên.

kotlin
// Import by name
import com.example.extensions.isEmail

// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail

// Call after import
val valid = "test@test.com".isEmail()

Đổi tên qua as hữu ích cho xung đột tên — ví dụ: nếu hai thư viện cung cấp extension function cùng tên cho cùng một kiểu. Trong trường hợp này, bạn có thể import một trong số chúng với tên khác và gọi nó bằng tên mới.

Phạm vi

Extension functions có thể được khai báo ở các cấp độ khác nhau:

Cấp độKhả năng hiển thịVí dụ
Top-levelToàn bộ dự án sau khi importfun String.isEmail()
Member extensionBên trong lớp sở hữuclass A { fun B.ext() }
LocalBên trong một hàmfun test() { fun String.ext() }

Member extension functions là trường hợp đặc biệt khi extension function được khai báo bên trong một lớp khác. Trong trường hợp này, cả bộ tiếp nhận (this của hàm) và các thành viên của lớp bên ngoài đều có thể truy cập được bên trong extension function.

Ưu tiên thành viên lớp hơn extension

Nếu một lớp có phương thức cùng chữ ký với extension function, thành viên lớp luôn được gọi. Extension function không bao giờ ghi đè phương thức lớp — đây là quyết định kiến trúc để ngăn chặn việc ghi đè ngẫu nhiên.

kotlin
class User {
    fun greet() = "Hello from class"
}

fun User.greet() = "Hello from extension"

fun main() {
    val user = User()
    println(user.greet()) // "Hello from class"
}

Ngay cả khi extension function được định nghĩa sau và có cùng chữ ký, trình biên dịch sẽ chọn phương thức của lớp. Trình biên dịch Kotlin đưa ra cảnh báo khi phát hiện tình huống này. Cách duy nhất để gọi extension function khi phương thức lớp tồn tại là gọi nó như một hàm thông thường: greet(user).

Ví dụ thực tế về extension functions

Trong phát triển Android, extension functions đã trở thành công cụ tiêu chuẩn để làm việc với View, Context và fragment. Thư viện Android KTX được xây dựng trên extension functions, cung cấp các wrapper tiện lợi cho API Android.

kotlin
// Extension function for working with View
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// Extension function for Context — toast
fun Context.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// Extension function as DSL builder
fun String.colorFormat(color: String): String =
    "$color$this\u001b[0m"

Extension function toast() làm cho các lệnh gọi phụ thuộc Context trở nên ngắn gọn: thay vì viết Toast.makeText(context, message, length).show(), chỉ cần viết context.toast("Văn bản"). Điều này giảm boilerplate và làm cho mã dễ đọc hơn.

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

Có thể ghi đè extension function trong lớp con không?

Không, extension functions sử dụng phân phối tĩnh. Nếu bạn khai báo extension function cho lớp cơ sở và cùng một hàm cho lớp con, hàm nào được gọi được xác định bởi kiểu tĩnh của biến tại thời điểm biên dịch, không phải bởi kiểu thực tế trong thời gian chạy.

Extension functions hoạt động như thế nào với kiểu nullable?

Bạn có thể khai báo extension functions cho kiểu tiếp nhận nullable: fun String?.isNullOrEmail(). Bên trong hàm như vậy, this có thể là null, vì vậy bạn phải sử dụng toán tử gọi an toàn hoặc kiểm tra null rõ ràng.

Extension functions có overhead về hiệu suất không?

Không, extension functions được biên dịch thành các phương thức tĩnh. Ở cấp độ bytecode, gọi extension function giống hệt như gọi phương thức tĩnh với bộ tiếp nhận là tham số đầu tiên. Không có phản chiếu hoặc phân phối động nào liên quan.

Có thể khai báo extension property cho một lớp không?

, extension properties hoạt động tương tự extension functions nhưng không thể lưu trữ trạng thái — chỉ có getter và setter. Ví dụ: val List.sum get() = reduce(Int::plus). Chúng cũng được biên dịch thành các phương thức getter/setter tĩnh.

Extension functions khác với phương thức trong lớp tiện ích Java như thế nào?

Trong Java bạn viết StringUtils.isEmail(str), còn trong Kotlin bạn viết str.isEmail(). Sự khác biệt không chỉ về cú pháp: extension functions hỗ trợ tự động hoàn thành trong IDE, cải thiện khả năng đọc của chuỗi gọi và cho phép IDE đề xuất các hàm liên quan cho một kiểu cụ thể. Java không có những khả năng này.

Tổng kết

  • Extension function — một phương thức với kiểu tiếp nhận, được gọi như thành viên của lớp mà không cần kế thừa
  • Phân phối tĩnh — chọn hàm dựa trên kiểu tĩnh, không phải kiểu thời gian chạy
  • Không overhead — được biên dịch thành phương thức tĩnh với bộ tiếp nhận là tham số đầu tiên
  • Import bắt buộc để sử dụng trong các tệp khác, bí danh qua as được hỗ trợ
  • Thành viên lớp được ưu tiên hơn extension functions có cùng chữ ký
  • Member extensions — extension được khai báo bên trong lớp có quyền truy cập vào các thành viên của nó
  • Sử dụng thực tế — tiện ích cho String, View, Context, bộ sưu tập và xây dựng DSL

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