Ограничения
Что не могут делать классы-значения
«Ограничения» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Понимание ограничений
Классы-значения мощны, но имеют ограничения. Понимание этих ограничений помогает выбрать подходящий инструмент и избежать неожиданностей.
Ровно одно свойство
Класс-значение оборачивает одно значение. Для объединения нескольких полей нужен обычный класс или data class.
// @JvmInline value class Range(val lo: Int, val hi: Int) // not allowed
data class Range(val lo: Int, val hi: Int)
fun main() {
println(Range(1, 10))
}Нет состояния инициализации помимо значения
Классы-значения не могут иметь полей хранения, кроме единственного свойства конструктора. Дополнительное состояние var с полем хранения не допускается.
@JvmInline
value class Counter(val value: Int) {
// val cached: Int = value * 2 // not allowed: extra backing field
val doubled: Int get() = value * 2 // computed is fine
}
fun main() {
println(Counter(3).doubled)
}Без наследования
Классы-значения неявно являются final. Они не могут наследовать другие классы, и от них нельзя наследовать. Они могут только реализовывать интерфейсы.
interface Named { val label: String }
@JvmInline
value class Tag(val text: String) : Named {
override val label: String get() = text
}
fun main() {
println(Tag("urgent").label)
}Цена упаковки возвращается
Обещание нулевых накладных расходов исчезает, когда значение используется как допускающий отсутствие значения, обобщённый или интерфейсный тип. Тогда компилятор упаковывает его в настоящий объект.
@JvmInline
value class Id(val raw: Int)
fun main() {
val list: List<Id> = listOf(Id(1), Id(2)) // boxed inside the list
println(list.map { it.raw })
}Нет ссылочной идентичности
Классы-значения не имеют стабильной ссылочной идентичности. Сравнение ссылок с помощью === бессмысленно, и их нельзя использовать там, где важна идентичность объекта, например в качестве блокировки синхронизации.
@JvmInline
value class Code(val value: Int)
fun main() {
val a = Code(5)
val b = Code(5)
println(a == b) // structural equality works
}Нет отложенной инициализации и делегирования
Нельзя применить lateinit к базовому значению, а классы-значения не поддерживают делегирование свойств своего единственного поля. Значение должно быть задано в конструкторе.
@JvmInline
value class Slug(val value: String)
fun main() {
val s = Slug("hello-world") // must be provided up front
println(s.value)
}Искажение имён на JVM
На JVM функции, принимающие параметры-классы-значения, получают искажённые имена в байткоде, чтобы избежать конфликтов. Это усложняет взаимодействие с Java: вызывающий код на Java может видеть такие функции некорректно.
@JvmInline
value class Hz(val value: Int)
fun tune(freq: Hz) = freq.value
fun main() {
println(tune(Hz(440))) // simple from Kotlin; mangled from Java
}Не для обёрток с несколькими полями
Если Вам понадобились два свойства, класс-значение — неподходящий инструмент. Используйте data class, приняв выделение памяти в обмен на более богатую структуру.
data class Coordinate(val lat: Double, val lng: Double)
fun main() {
println(Coordinate(41.0, 29.0))
}Итоги ограничений
Классы-значения не могут:
- Содержать более одного свойства
- Иметь дополнительные поля хранения или
lateinit - Наследовать классы или быть их родителями
- Обеспечивать ссылочную идентичность
- Всегда избегать упаковки
Осознанный выбор
Используйте класс-значение для единственного проверяемого типобезопасного значения. Для сгруппированных данных используйте класс данных. Псевдоним типа выбирайте ради удобства чтения без повышения безопасности. У каждого инструмента своё назначение.
Быстрая проверка
Проверьте, насколько хорошо Вы понимаете ограничения классов-значений.
Итоги
Вы изучили ограничения классов-значений:
- Одно свойство и отсутствие дополнительных полей хранения
- Нет наследования и идентичности
- При использовании в допускающем отсутствие значения, обобщённом или интерфейсном контексте упаковка возвращается
- Искажение имён на JVM усложняет взаимодействие с Java
Подбирайте инструмент под задачу.
Часто задаваемые вопросы
Урок «Ограничения» бесплатный?
Да — полный текст урока «Ограничения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «Ограничения»?
Что не могут делать классы-значения Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Ограничения»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.