companion object — مکانیزم زبان Kotlin است که کلمه کلیدی static از Java را برای اعلام اعضای ایستای کلاس جایگزین میکند. در Kotlin static داخلی وجود ندارد — در عوض از اعلان object با اصلاحکننده companion در داخل کلاس استفاده میشود. به گفته JetBrains, 2025، companion object امکان فراخوانی متدها و ویژگیها را از طریق نام کلاس فراهم میکند که آنها را به معادل کامل اعضای ایستا در اکوسیستم Java تبدیل میکند.
نکات اصلی
companion object — نوع خاصی از object declaration در زبان Kotlin است که با کلمه کلیدی companion علامتگذاری میشود. این امکان را فراهم میکند تا اعضایی را اعلام کنید که به کلاس تعلق دارند نه به نمونههای آن — یعنی اعضای ایستا در اصطلاحات Java. برخلاف Java که در آن static اصلاحکننده برای فیلدها و متدهای جداگانه است، در Kotlin اعضای ایستا در داخل یک object همراه گروهبندی میشوند.
هر کلاس در Kotlin میتواند دقیقاً یک companion object داشته باشد. اعضای این object از طریق نام کلاس بدون ایجاد نمونه قابل دسترسی هستند: MyClass.method(). در عین حال از نظر بایتکد، companion object به یک کلاس داخلی جداگانه کامپایل میشود و متدهای آن با استفاده از annotation @JvmStatic به متدهای ایستای کلاس خارجی تبدیل میشوند.
بر اساس Google I/O 2024، companion object به الگوی اصلی برای متدهای کارخانهای، ثابتها و توابع کمکی در برنامههای مدرن Android با Kotlin تبدیل شده است. توسعهدهندگان آن را به جای کلاسهای کمکی با توابع بستهای انتخاب میکنند، زیرا companion object ارتباط منطقی با کلاس مالک را حفظ میکند.
از companion object برای گروهبندی زمینه ایستا — ثابتها، کارخانهها، متدهای کمکی که منطقاً به کلاس مربوط هستند اما نیاز به نمونه ندارند — استفاده کنید.
نحو پایه companion object ساده است: کلمه کلیدی companion قبل از اعلان object در داخل کلاس قرار میگیرد. اگر نامی مشخص نشود، object نام Companion را میگیرد که میتوان به صورت صریح یا ضمنی به آن دسترسی داشت.
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 }
}
// دسترسی از طریق نام کلاس
val table = User.TABLE_NAME
val user = User.create("Alice")
در مثال بالا TABLE_NAME و create() از طریق User.TABLE_NAME و User.create() قابل دسترسی هستند. به اصلاحکننده const برای ثابتهای اولیه توجه کنید — این اصلاحکننده درج مقدار را در مرحله کامپایل تضمین میکند، مشابه Java static final برای primitives و String.
اگر companion object نام ندارد، میتوان از طریق نام خودکار Companion به آن دسترسی داشت: User.Companion.TABLE_NAME. در عمل این به ندرت لازم است، اما هنگام فراخوانی از کد Java یا هنگام استفاده از reflection مفید است.
// هر دو فراخوانی معادل هستند
User.create("Bob")
User.Companion.create("Bob")
// دریافت ارجاع به companion object
val companion: User.Companion = User
companion object میتواند نام داشته باشد که خوانایی کد را بهبود میبخشد و امکان دسترسی به آن را با یک نام قابل فهم فراهم میکند. Factory — رایجترین نام برای companion object است که به عنوان کارخانه استفاده میشود.
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")
}
}
// دسترسی از طریق نام Factory
val response = HttpResponse.Factory.ok("data")
companion object نامدار به مستندسازی هدف اعضای ایستا کمک میکند. در بازسازی کد، نام کد را خود-مستندساز میکند — توسعهدهنده بلافاصله میبیند که Factory نمونههای HttpResponse را ایجاد میکند.
یک کلاس فقط میتواند یک companion object داشته باشد. این تفاوت اساسی با Java است که در آن میتوان هر تعداد فیلد و متد ایستا بدون گروهبندی اعلام کرد. اگر چندین گروه منطقی از اعضای ایستا نیاز است، از object declarations تو در تو بدون companion استفاده کنید.
بر اساس مستندات رسمی Kotlin (Kotlin Docs, 2025)، محدودیت یک companion object با طراحی زبان مرتبط است: اعضای ایستا باید با کلاس ارتباط تنگاتنگ داشته باشند و یک object همراه مرز روشن این ارتباط را فراهم میکند. اگر اعضای ایستا بیش از حد زیاد باشند — این سیگنالی برای بازسازی کلاس است.
در Kotlin، اینترفیسها نیز میتوانند companion object داشته باشند. این امکان تعریف متدها و ثابتهای ایستا را مستقیماً در داخل اینترفیس فراهم میکند که در Java امکانپذیر نیست. این رویکرد اغلب برای اعلام ثابتهای مرتبط با اینترفیس یا متدهای کارخانهای استفاده میشود.
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)
}
}
در این مثال BASE_URL، TIMEOUT_MS و create() به اینترفیس ApiService تعلق دارند نه به پیادهسازیهای آن. این راحت است: تمام ثابتها و منطق کارخانهای برای ایجاد نمونه سرویس API در یک مکان — در داخل خود اینترفیس — قرار دارند.
پیادهسازیهای اینترفیس اعضای companion object را به ارث نمیبرند — فقط از طریق نام اینترفیس فراخوانی میشوند: ApiService.BASE_URL.
هنگام فراخوانی متدهای companion object از کد Java، آنها به طور پیشفرض به عنوان متدهای کلاس داخلی Companion قابل دسترسی هستند، نه به عنوان متدهای ایستای کلاس خارجی. برای صدور آنها به عنوان متدها و فیلدهای static واقعی برای Java، از annotation @JvmStatic برای متدها و @JvmField برای فیلدها استفاده کنید.
| Annotation | کاربرد | نتیجه در Java |
|---|---|---|
| @JvmStatic | متدهای companion object | متد ایستا: ClassName.method() |
| @JvmField | فیلدهای companion object | فیلد ایستا: ClassName.field |
| const | انواع اولیه و String | ثابت درونخطی: کامپایلر مقدار را جایگزین میکند |
| بدون annotation | متدها/فیلدهای پیشفرض | Companion.method() / Companion.field |
class MathUtils {
companion object {
const val PI = 3.14159
@JvmStatic
fun square(x: Int): Int = x * x
@JvmField
val TAG: String = "MathUtils"
}
}
// در Java به صورت MathUtils.square(5), MathUtils.TAG فراخوانی میشود
استفاده از @JvmStatic برای تمام متدهای عمومی companion object که باید از کد Java قابل دسترسی باشند توصیه میشود. const فقط برای انواع اولیه و String اعمال میشود — برای سایر انواع از @JvmField استفاده کنید.
در پروژههای واقعی، companion object برای چندین سناریوی استاندارد استفاده میشود. متدهای کارخانهای — رایجترین الگو: به جای سازندههای متعدد با امضاهای مختلف، از متدهای کارخانهای نامدار در companion object استفاده میشود.
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 در NetworkResult متد کارخانهای loading() را ارائه میدهد که نمونه Loading را با نوع generic صحیح ایجاد میکند. بدون این متد، باید مستقیماً از طریق Loading() نمونه ایجاد میکردید که پیادهسازی داخلی را آشکار میکند.
اغلب از companion object برای ذخیره ثابتهای مختص کلاس استفاده میشود. ثابتهای اعلام شده با const val در مرحله کامپایل درج میشوند که سربار صفر در زمان اجرا ایجاد میکند.
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
سوالات متداول
ordinary object — یک singleton کامل است که مستقل از کلاس وجود دارد. companion object — object در داخل کلاس است که اعضای آن از طریق نام کلاس خارجی فراخوانی میشوند. در بایتکد، companion object به یک کلاس داخلی ایستا تبدیل میشود و object معمولی به یک کلاس singleton مستقل تبدیل میشود.
خیر، companion object توسط زیرکلاسها به ارث برده نمیشود. اگر کلاس B از کلاس A ارثبری کند، B.method() متدی را از companion object کلاس A فراخوانی نمیکند — باید از طریق A.method() دسترسی داشت. این رفتار مطابق با متدهای ایستا در Java است.
به طور پیشفرض — از طریق ClassName.Companion.method(). برای فراخوانی به عنوان متد ایستا، @JvmStatic را به متد companion object اضافه کنید. برای فیلدها از @JvmField استفاده کنید یا آنها را با const val برای انواع اولیه اعلام کنید.
توسعهدهندگان Kotlin تصمیم گرفتند به نفع companion object از static صرفنظر کنند تا کار با objectها یکپارچه باشد. static در Java اصول OOP را نقض میکند زیرا متدهای ایستا با نمونه مرتبط نیستند. companion object — یک object درجه یک است که میتوان آن را منتقل کرد، با توابع توسعهدهنده گسترش داد و اینترفیسها را پیادهسازی کرد.
هنگام استفاده از const val برای انواع اولیه و String — سربار صفر، مقدار در بایتکد درج میشود. برای متدها سرباری وجود ندارد اگر متد inline نباشد. در بیشتر موارد، companion object تأثیر قابل اندازهگیری بر عملکرد ندارد. از @JvmStatic فقط برای API عمومی که از Java فراخوانی میشود استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید