Menu
Coddy logo textTech

Константы с const

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 14 из 57.

const val объявляет константу времени компиляции: значение, фиксированное во время компиляции программы. Она допускается на верхнем уровне файла и внутри object или companion object, и только для базовых типов, таких как Int, Double и Boolean, а также для String:

const val MAX_PLAYERS = 4

class Lobby {
    val players = mutableListOf<String>()

    fun canStart() = players.size in MIN_PLAYERS..MAX_PLAYERS

    companion object {
        const val MIN_PLAYERS = 2
    }
}

Внутри main:

val lobby = Lobby()
lobby.players += listOf("ada", "bo")
println(lobby.canStart())
println("${Lobby.MIN_PLAYERS}-$MAX_PLAYERS players")

Результат:

true
2-4 players

Константы именуются в формате UPPER_SNAKE_CASE. const строже, чем val: значение должно быть известно без выполнения какого-либо кода, поэтому вызов функции или список недопустимы. val в объекте также является общим и доступным только для чтения и принимает любое значение:

const val GREETING = "Hello"                  // строковый литерал
const val TIMEOUT_MS = 30 * 1000              // вычислено из литералов
// const val START = System.nanoTime()        // ошибка: не константа
// const val DAYS = listOf("Mon", "Tue")      // ошибка: только базовые типы и String
object Defaults {
    val DAYS = listOf("Mon", "Tue")           // общий val только для чтения
}

Объект-компаньон также может хранить общее изменяемое состояние. Каждый экземпляр читает одно и то же свойство и изменяет его, что подходит для счётчиков и кэшей. Этот класс хранит по одному экземпляру для каждого имени, поэтому повторный запрос возвращает тот же объект (=== проверяет, что две ссылки указывают на один объект):

class Tile private constructor(val name: String) {
    companion object {
        private val cache = mutableMapOf<String, Tile>()
        val created: Int
            get() = cache.size

        fun of(name: String) = cache.getOrPut(name) { Tile(name) }
    }
}

Внутри main:

val a = Tile.of("grass")
val b = Tile.of("grass")
Tile.of("water")
println(a === b)
println(Tile.created)

Вывод:

true
2

Верхнего уровня или в объекте-компаньоне? Константа или функция верхнего уровня принадлежит своему файлу и используется по своему простому имени в каждом файле пакета. Поместите константу в объект-компаньон, если она относится к одному классу: вызывающий код пишет Lobby.MIN_PLAYERS, что показывает, откуда происходит значение, а private const val остаётся внутри этого класса.

// Limits.kt
const val MAX_UPLOAD_MB = 25                 // используется везде как MAX_UPLOAD_MB

// Lobby.kt
class Lobby {
    companion object {
        const val MIN_PLAYERS = 2            // используется как Lobby.MIN_PLAYERS
        private const val ROOM_PREFIX = "L-" // только внутри Lobby
    }
}
challenge icon

Задание

Легко

Завершите класс Order(val item: String, val qty: Int). Его сопутствующий объект содержит константы MAX_QTY = 10 и PREFIX = "ORD", приватный счётчик для следующего номера заказа, общее доступное только для чтения значение totalItems и вычисляемое количество созданных заказов count. Блок init требует, чтобы количество находилось между 1 и MAX_QTY, с сообщением quantity must be 1-10 (сформированным на основе константы), затем присваивает заказу идентификатор ORD-001, ORD-002, ... и добавляет его количество к totalItems. label() возвращает ORD-001 tea x2.

Предоставленный код выводит max per order: 10, затем считывает строки tea 2 и выводит метку или сообщение об ошибке, а в конце — orders: 2, items: 5. Отклонённые заказы не занимают номер.

Ваш код должен находиться в Order.kt. В Main.kt содержится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    val input = generateSequence(::readLine).toList()
    println("max per order: ${Order.MAX_QTY}")
    for (line in input) {
        val (item, qty) = line.split(" ")
        try {
            println(Order(item, qty.toInt()).label())
        } catch (e: IllegalArgumentException) {
            println(e.message)
        }
    }
    println("orders: ${Order.count}, items: ${Order.totalItems}")
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin