← Назад к вопросам
Когда инициализируется companion object?
2.0 Middle🔥 121 комментариев
#Kotlin основы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда инициализируется companion object
companion object в Kotlin — это объект, который инициализируется ленивым образом (lazy), но существует в единственном экземпляре (синглтон) для каждого класса. Момент его инициализации — важный момент для понимания производительности и поведения приложения.
Основной момент инициализации
companion object инициализируется при первом обращении к нему или к полям/методам класса.
class MyClass {
companion object {
init {
Log.d("TAG", "companion object initialized")
}
const val CONSTANT = "value"
fun staticMethod() {
Log.d("TAG", "static method called")
}
}
}
// Инициализация companion object:
val constant = MyClass.CONSTANT // Инициализация + возврат
MyClass.staticMethod() // Еще одна инициализация? Нет!
MyClass.staticMethod() // Уже инициализирован
Точный момент инициализации
class DatabaseConfig {
companion object {
init {
println("DatabaseConfig companion object init")
}
val database: Database = Database() // Тяжелая инициализация
const val CONNECTION_POOL_SIZE = 10
}
}
fun main() {
println("1. Before accessing companion")
// Момент инициализации companion object
val poolSize = DatabaseConfig.CONNECTION_POOL_SIZE
// Выведет: DatabaseConfig companion object init
println("2. After accessing constant")
// Повторное обращение — companion уже инициализирован
DatabaseConfig.database.query("SELECT * FROM users")
println("3. After database query")
}
// Вывод:
// 1. Before accessing companion
// DatabaseConfig companion object init
// 2. After accessing constant
// 3. After database query
Отличие const val и val в companion object
class Constants {
companion object {
const val CONST_VALUE = "const" // Compile-time constant
val VAL_VALUE = "val" // Runtime value
init {
println("Companion object initialized")
}
}
}
fun main() {
// const val подменяется compile time — companion НЕ инициализируется
println(Constants.CONST_VALUE)
println(Constants.CONST_VALUE) // companion все еще не инициализирован!
// val требует runtime значение — companion ИНИЦИАЛИЗИРУЕТСЯ
println(Constants.VAL_VALUE) // Выведет: Companion object initialized
println(Constants.VAL_VALUE) // companion уже инициализирован
}
Thread-safety
companion object инициализируется потокобезопасным образом благодаря JVM. Это означает, что даже при многопоточном доступе инициализация произойдет ровно один раз.
class ThreadSafeConfig {
companion object {
val config: String = "Config"
init {
Log.d("TAG", "Init on thread: ${Thread.currentThread().name}")
}
}
}
fun main() {
// Запускаем 5 потоков которые обращаются к companion
repeat(5) {
thread {
val config = ThreadSafeConfig.config
// Все потоки увидят инициализацию только один раз
}
}
}
Практические примеры
1. Lazy инициализация тяжелых объектов
class ApiService {
companion object {
// Retrofit инициализируется только при первом обращении
val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl("https://api.example.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val api: UserApi = retrofit.create(UserApi::class.java)
}
}
// Использование
fun loadUsers() {
// Retrofit инициализируется здесь (если еще не инициализирован)
val users = ApiService.api.getUsers()
}
2. Синглтон паттерн
class Logger {
companion object {
val instance = Logger() // Создается один раз
fun log(message: String) {
println(message)
}
}
}
// Или более явно:
class Preferences private constructor() {
companion object {
@Volatile
private var instance: Preferences? = null
fun getInstance() =
instance ?: synchronized(this) {
instance ?: Preferences().also { instance = it }
}
}
}
3. Factory методы
data class User(
val id: String,
val name: String,
val email: String
) {
companion object {
// Factory методы для создания User
fun fromJson(json: String): User {
// Парсим JSON
return User("", "", "")
}
fun default(): User {
return User("0", "Unknown", "unknown@example.com")
}
}
}
// Использование
val user1 = User.fromJson(jsonString)
val user2 = User.default()
Когда companion object не инициализируется
class Example {
companion object {
const val CONSTANT = 1 // const, подменяется компилятором
init {
println("Will not print!")
}
}
}
// companion object НЕ будет инициализирован
// потому что CONSTANT это const val
val value = Example.CONSTANT
// Но companion БУДЕТ инициализирован если обратимся к другому члену
val constant = Example::class.java.getDeclaredField("Companion")
Инициализация при загрузке класса
class OnLoadExample {
companion object {
init {
println("Companion init")
}
}
init {
println("Instance init")
}
}
fun main() {
println("1. Before class loading")
// Загружаем класс (companion еще не инициализирован)
val clazz = OnLoadExample::class
println("2. Class loaded")
// Создаем экземпляр (companion инициализируется если еще не инициализирован)
val instance = OnLoadExample()
println("3. Instance created")
// Обращаемся к companion через класс
OnLoadExample::class.java.simpleName
println("4. Class property accessed")
}
// Вывод (companion инициализируется при создании первого экземпляра):
// 1. Before class loading
// 2. Class loaded
// Companion init
// Instance init
// 3. Instance created
// 4. Class property accessed
Порядок инициализации в классе
class InitOrder {
// Шаг 1: Инициализируются свойства класса
val classProperty = "class"
// Шаг 2: init блоки выполняются
init {
println("Class init 1")
}
init {
println("Class init 2")
}
// Companion object инициализируется РАНЬШЕ первого экземпляра класса
companion object {
val companionProperty = "companion"
init {
println("Companion init")
}
}
// Шаг 3: Вторичный конструктор (если есть)
constructor(name: String) : this() {
println("Secondary constructor")
}
}
fun main() {
// Companion инициализируется ДО экземпляра
val instance = InitOrder()
}
// Вывод:
// Companion init
// Class init 1
// Class init 2
Performance соображения
// ПЛОХО — тяжелая инициализация в companion
class HeavyClass {
companion object {
val hugeDatabase = loadMillionRecords() // Займет время!
val complexCalculation = expensiveCompute() // Займет время!
}
}
// ХОРОШО — ленивая инициализация
class LightClass {
companion object {
val database: Database by lazy { loadMillionRecords() }
val calculation: Int by lazy { expensiveCompute() }
}
}
Итог
companion object инициализируется:
- При первом обращении к полю или методу (кроме const)
- При создании первого экземпляра класса (если companion содержит val)
- Потокобезопасно благодаря JVM
- Ровно один раз
Для оптимизации:
- Используй const val для compile-time констант
- Используй lazy для тяжелых инициализаций
- Помни что companion инициализируется перед первым экземпляром
- Избегай тяжелых инициализаций в companion если класс часто используется