0Pricing
Kotlin Academy · Урок

Встраиваемые классы-значения

Оборачивайте значения без дополнительных затрат

«Встраиваемые классы-значения» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Проблема обёртки

Обёртка примитивного типа в класс повышает типобезопасность, но обычно требует выделения памяти в куче. Классы-значения обеспечивают безопасность, а компилятор по возможности устраняет обёртку.

Объявление класса-значения

Отметьте класс @JvmInline value class ровно одним свойством в основном конструкторе. Он ведёт себя как отдельный тип.

@JvmInline
value class Password(val value: String)

fun main() {
    val p = Password("secret")
    println(p.value)
}

Отдельный тип

В отличие от псевдонима типа, класс-значение является отдельным типом. Нельзя случайно передать обычный String там, где ожидается Password.

@JvmInline
value class Password(val value: String)

fun login(p: Password) = println("login with " + p.value)

fun main() {
    login(Password("hunter2"))
}

Нулевые накладные расходы

Во время выполнения компилятор обычно встраивает базовое значение, поэтому дополнительного выделения памяти под объект не происходит. В большинстве путей выполнения Password представлен обычным String.

@JvmInline
value class Meters(val amount: Double)

fun main() {
    val d = Meters(5.0)
    println(d.amount) // stored inline as a Double
}

Методы и свойства

Классы-значения могут содержать функции и вычисляемые свойства, как и обычные классы.

@JvmInline
value class Celsius(val degrees: Double) {
    fun toFahrenheit() = degrees * 9 / 5 + 32
}

fun main() {
    println(Celsius(100.0).toFahrenheit())
}

Блоки инициализации для проверки

Блок init позволяет проверить обёрнутое значение при создании, гарантируя, что каждый экземпляр корректен.

@JvmInline
value class Percentage(val value: Int) {
    init {
        require(value in 0..100) { "out of range" }
    }
}

fun main() {
    println(Percentage(75).value)
}

Вычисляемые свойства

Можно добавлять вычисляемые свойства, получаемые из единственного хранимого значения, чтобы логика оставалась рядом с типом.

@JvmInline
value class Seconds(val value: Int) {
    val asMinutes: Double get() = value / 60.0
}

fun main() {
    println(Seconds(120).asMinutes)
}

Реализация интерфейсов

Классы-значения могут реализовывать интерфейсы, что позволяет использовать их полиморфно. Обратите внимание: при обращении к ним как к типу интерфейса может потребоваться упаковка.

interface Describable {
    fun describe(): String
}

@JvmInline
value class Money(val cents: Int) : Describable {
    override fun describe() = "$" + cents / 100.0
}

fun main() {
    println(Money(1599).describe())
}

Когда происходит упаковка

Обёртка снова появляется при упаковке, когда значение используется как допускающее отсутствие значения, как интерфейс или в обобщённой коллекции. В таких случаях компилятор создаёт настоящий объект — это по-прежнему корректно, но менее эффективно.

@JvmInline
value class Id(val raw: Int)

fun main() {
    val direct = Id(1)        // inlined
    val boxed: Id? = Id(2)    // boxed because nullable
    println("" + direct.raw + " " + boxed?.raw)
}

Класс-значение и класс данных

data class всегда требует выделения памяти и может содержать несколько свойств. value class содержит ровно одно свойство и рассчитан на нулевые накладные расходы. Выбирайте классы-значения для тонких типобезопасных обёрток над единственным значением.

Почему это важно

Классы-значения позволяют:

  • Не допускать путаницы между параметрами одинакового типа
  • Привязывать проверку к примитивному значению
  • Обычно не платить за это во время выполнения

Это типобезопасное решение там, где псевдонимов недостаточно.

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете встраиваемые классы-значения.

Итоги

Вы изучили встраиваемые классы-значения:

  • Объявляются с помощью @JvmInline value class и содержат одно свойство
  • Являются отдельным типом с обычно нулевыми накладными расходами
  • Поддерживают методы, вычисляемые свойства и проверку в init
  • Упаковка происходит при использовании значения в допускающем отсутствие значения, обобщённом или интерфейсном контексте

Далее: практические варианты использования.

Часто задаваемые вопросы

Урок «Встраиваемые классы-значения» бесплатный?

Да — полный текст урока «Встраиваемые классы-значения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Встраиваемые классы-значения»?

Оборачивайте значения без дополнительных затрат Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Встраиваемые классы-значения»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

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

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