0Pricing
Kotlin Academy · Урок

Ограничения

Что не могут делать классы-значения

«Ограничения» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. Псевдонимы типов
  2. Встраиваемые классы-значения
  3. Варианты использования
  4. Ограничения
← Назад к Kotlin Academy