Companion object 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

companion object là một cơ chế của ngôn ngữ Kotlin thay thế từ khóa static của Java để khai báo thành viên tĩnh của lớp. Kotlin không có static tích hợp — thay vào đó, sử dụng khai báo object với từ khóa companion bên trong một lớp. Theo JetBrains, 2025, companion object cho phép gọi phương thức và thuộc tính thông qua tên lớp, khiến chúng trở thành tương đương hoàn toàn với thành viên tĩnh trong hệ sinh thái Java.

Những Điểm Chính

  • companion object là khai báo object bên trong lớp với từ khóa companion, thay thế Java static
  • Thành viên tĩnh có thể truy cập qua tên lớp mà không cần tạo instance: ClassName.method()
  • Companion object có tên cho phép đặt tên cho đối tượng để mã dễ đọc hơn
  • Interface trong Kotlin cũng có thể chứa companion object với triển khai phương thức
  • @JvmStatic — chú thích để xuất phương thức companion object thành phương thức static thực sự trong Java

Companion object trong Kotlin là gì?

companion object là một loại đặc biệt của khai báo object trong Kotlin được đánh dấu bằng từ khóa companion. Nó cho phép khai báo các thành viên thuộc về lớp thay vì các instance của nó — tức là thành viên tĩnh theo thuật ngữ Java. Khác với Java, nơi static là bổ từ cho các trường và phương thức riêng lẻ, trong Kotlin các thành viên tĩnh được nhóm bên trong một đối tượng đồng hành duy nhất.

Bất kỳ lớp nào trong Kotlin cũng có thể chứa chính xác một companion object. Các thành viên của đối tượng này có thể truy cập qua tên lớp mà không cần tạo instance: MyClass.method(). Về mặt bytecode, companion object được biên dịch thành một lớp bên trong riêng biệt và các phương thức của nó trở thành phương thức tĩnh của lớp bên ngoài khi sử dụng chú thích @JvmStatic.

Theo Google I/O 2024, companion object đã trở thành mẫu chính cho phương thức factory, hằng số và hàm tiện ích trong các ứng dụng Android hiện đại viết bằng Kotlin. Các nhà phát triển chọn nó thay vì các lớp tiện ích với hàm cấp gói vì companion object duy trì kết nối logic với lớp sở hữu.

Sử dụng companion object để nhóm ngữ cảnh tĩnh — hằng số, factory, phương thức trợ giúp thuộc về mặt logic với lớp nhưng không yêu cầu instance.

Cú pháp companion object: khai báo và truy cập

Cú pháp cơ bản của companion object rất đơn giản: từ khóa companion được đặt trước khai báo object bên trong lớp. Nếu không có tên nào được chỉ định, đối tượng sẽ nhận tên Companion, có thể được truy cập một cách tường minh hoặc ngầm định.

kotlin
class User {
    companion object {
        const val TABLE_NAME = "users"

        fun create(name: String): User {
            return User(name)
        }
    }

    val name: String
    constructor(name: String) { this.name = name }
}

// Access via class name
val table = User.TABLE_NAME
val user = User.create("Alice")

Trong ví dụ trên, TABLE_NAME và create() có thể truy cập qua User.TABLE_NAME và User.create(). Hãy chú ý bổ từ const cho hằng số nguyên thủy — nó đảm bảo nội suy giá trị tại thời điểm biên dịch, tương tự Java static final cho kiểu nguyên thủy và String.

Truy cập ngầm định và tường minh qua Companion

Nếu companion object không có tên, có thể truy cập nó qua tên tự động Companion: User.Companion.TABLE_NAME. Trong thực tế điều này hiếm khi cần thiết, nhưng hữu ích khi gọi từ mã Java hoặc khi sử dụng reflection.

kotlin
// Both calls are equivalent
User.create("Bob")
User.Companion.create("Bob")

// Getting reference to companion object
val companion: User.Companion = User

Companion object có tên và đặc điểm

Companion object có thể có tên, giúp cải thiện khả năng đọc mã và cho phép truy cập nó bằng tên có ý nghĩa. Factory là tên phổ biến nhất cho companion object được sử dụng làm factory.

kotlin
class HttpResponse {
    companion object Factory {
        fun ok(body: String): HttpResponse = HttpResponse(200, body)
        fun notFound(): HttpResponse = HttpResponse(404, "Not Found")
        fun serverError(): HttpResponse = HttpResponse(500, "Internal Error")
    }
}

// Access via Factory name
val response = HttpResponse.Factory.ok("data")

Companion object có tên giúp ghi lại mục đích của các thành viên tĩnh. Trong tái cấu trúc, tên làm cho mã tự ghi chép — nhà phát triển thấy ngay rằng Factory tạo các instance của HttpResponse.

Giới hạn: chính xác một companion object trên mỗi lớp

Một lớp chỉ có thể chứa một companion object. Đây là sự khác biệt cơ bản so với Java, nơi bạn có thể khai báo bất kỳ số lượng trường và phương thức tĩnh nào mà không cần nhóm. Nếu cần nhiều nhóm logic của thành viên tĩnh, hãy sử dụng khai báo object lồng nhau mà không có companion.

Theo tài liệu chính thức của Kotlin (Kotlin Docs, 2025), giới hạn một companion object được thúc đẩy bởi thiết kế ngôn ngữ: các thành viên tĩnh phải được gắn kết chặt chẽ với lớp và một đối tượng đồng hành cung cấp ranh giới rõ ràng cho kết nối này. Nếu có quá nhiều thành viên tĩnh, đó là tín hiệu để tái cấu trúc lớp.

Companion object trong interface

Trong Kotlin, interface cũng có thể chứa companion object. Điều này cho phép định nghĩa phương thức tĩnh và hằng số trực tiếp bên trong interface, điều không thể trong Java. Cách tiếp cận này thường được sử dụng để khai báo hằng số liên quan đến interface hoặc phương thức factory.

kotlin
interface ApiService {
    companion object {
        const val BASE_URL = "https://api.example.com"
        const val TIMEOUT_MS = 5000

        fun create(client: OkHttpClient): ApiService =
            Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .build()
                .create(ApiService::class.java)
    }
}

Trong ví dụ này, BASE_URL, TIMEOUT_MS và create() thuộc về interface ApiService, không phải các triển khai của nó. Điều này thuận tiện: tất cả hằng số và logic factory để tạo instance dịch vụ API nằm ở một nơi — bên trong chính interface.

Các triển khai interface không kế thừa các thành viên của companion object — chúng chỉ được gọi qua tên interface: ApiService.BASE_URL.

@JvmStatic và @JvmField: tương thích Java

Khi gọi phương thức companion object từ mã Java, theo mặc định chúng có thể truy cập như phương thức của lớp Companion lồng nhau, không phải phương thức tĩnh của lớp bên ngoài. Để xuất chúng như phương thức và trường tĩnh thực sự cho Java, hãy sử dụng chú thích @JvmStatic cho phương thức và @JvmField cho trường.

Chú thíchÁp dụngKết quả trong Java
@JvmStaticPhương thức companion objectPhương thức tĩnh: ClassName.method()
@JvmFieldTrường companion objectTrường tĩnh: ClassName.field
constKiểu nguyên thủy và StringHằng số nội suy: trình biên dịch thay thế giá trị
không chú thíchPhương thức/trường mặc địnhCompanion.method() / Companion.field
kotlin
class MathUtils {
    companion object {
        const val PI = 3.14159

        @JvmStatic
        fun square(x: Int): Int = x * x

        @JvmField
        val TAG: String = "MathUtils"
    }
}

// Called in Java as MathUtils.square(5), MathUtils.TAG

Khuyến nghị sử dụng @JvmStatic cho tất cả phương thức companion object công khai cần truy cập từ mã Java. const chỉ áp dụng cho kiểu nguyên thủy và String — cho các kiểu khác hãy sử dụng @JvmField.

Ví dụ thực tế về sử dụng companion object

Trong các dự án thực tế, companion object được sử dụng cho nhiều kịch bản tiêu chuẩn. Phương thức factory là mẫu phổ biến nhất: thay vì nhiều hàm tạo với chữ ký khác nhau, các phương thức factory có tên được sử dụng trong companion object.

kotlin
sealed class NetworkResult<out T> {
    data class Success<out T>(val data: T) : NetworkResult<T>()
    data class Error(val message: String) : NetworkResult<Nothing>()

    companion object {
        fun loading<T>(): NetworkResult<T> =
            Loading()
    }
}

private class Loading<T> : NetworkResult<T>()

companion object trong NetworkResult cung cấp phương thức factory loading() tạo instance Loading với kiểu generic chính xác. Nếu không có phương thức này, bạn phải tạo instance trực tiếp qua Loading(), điều này làm lộ triển khai nội bộ.

Hằng số và thẻ ghi log

companion object thường được sử dụng để lưu trữ hằng số cụ thể của lớp. Các hằng số được khai báo với const val được nội suy tại thời điểm biên dịch, mang lại chi phí bằng không khi chạy.

kotlin
class UserRepository {
    companion object {
        private const val TAG = "UserRepository"
        private const val CACHE_SIZE = 100
        private const val DEFAULT_PAGE_SIZE = 20
    }
}

Câu Hỏi Thường Gặp

Companion object khác với object thông thường trong Kotlin như thế nào?

Object thông thường là một singleton hoàn chỉnh tồn tại độc lập với lớp. companion object là một object bên trong lớp có các thành viên được gọi qua tên lớp bên ngoài. Trong bytecode, companion object trở thành một lớp lồng nhau tĩnh, trong khi object thông thường trở thành một lớp singleton tự trị.

Có thể kế thừa companion object không?

Không, companion object không được kế thừa bởi các lớp con. Nếu lớp B mở rộng lớp A, thì B.method() sẽ không gọi phương thức từ companion object của lớp A — bạn cần gọi qua A.method(). Điều này phù hợp với hành vi của phương thức tĩnh trong Java.

Làm thế nào để gọi companion object từ mã Java?

Theo mặc định — qua ClassName.Companion.method(). Để gọi nó như phương thức tĩnh, thêm @JvmStatic vào phương thức companion object. Cho trường, sử dụng @JvmField hoặc khai báo chúng với const val cho kiểu nguyên thủy.

Tại sao Kotlin không có từ khóa static?

Các nhà phát triển Kotlin đã quyết định loại bỏ static để chuyển sang companion object nhằm làm việc thống nhất với các đối tượng. static trong Java vi phạm nguyên tắc OOP vì phương thức tĩnh không gắn với instance. companion object là đối tượng hạng nhất có thể được truyền, mở rộng với hàm mở rộng và triển khai interface.

Companion object có ảnh hưởng đến hiệu suất không?

Khi sử dụng const val cho kiểu nguyên thủy và String — chi phí bằng không, giá trị được nội suy vào bytecode. Cho phương thức, không có chi phí trừ khi phương thức là inline. Trong hầu hết trường hợp, companion object không tạo ra tác động đo lường được đến hiệu suất. Chỉ sử dụng @JvmStatic cho API công khai được gọi từ Java.

Tổng Kết

  • companion object là cơ chế Kotlin để khai báo thành viên tĩnh của lớp, thay thế Java static
  • Một companion object trên mỗi lớp — nhóm tất cả phần tử tĩnh trong một đối tượng đồng hành
  • Truy cập qua tên lớp: ClassName.method() hoặc ClassName.Companion.method()
  • @JvmStatic và @JvmField xuất thành viên thành phương thức và trường tĩnh thực sự cho Java
  • const val cho kiểu nguyên thủy và String — nội suy giá trị tại biên dịch với chi phí bằng không
  • Interface cũng hỗ trợ companion object cho hằng số và phương thức factory
  • Companion object có tên cải thiện khả năng đọc mã và tự ghi chép mục đích của thành viên tĩnh

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