Классы перечислений
Классы перечислений представляют фиксированный набор возможных значений. Используйте класс перечисления, если значение может быть только одним из нескольких заранее определённых вариантов, например доступных состояний или режимов.
Каждое значение в классе перечисления называется константой перечисления. Константы перечисления ведут себя как объекты-одиночки типа класса перечисления, поэтому у них могут быть свойства, функции и собственное поведение.
Классы перечислений лучше всего подходят, когда все возможные значения известны заранее и имеют одинаковую структуру. Если вам нужно хранить разные данные или использовать разную структуру для каждого случая, используйте закрытые классы или интерфейсы.
Объявление классов перечислений
Чтобы создать класс перечисления, используйте ключевое слово enum и следуйте обычному синтаксису класса, добавив тело в фигурных скобках. Внутри тела класса перечислите константы перечисления через запятую:
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
В этом примере Direction — это класс перечисления, а NORTH, SOUTH, WEST и EAST — константы перечисления.
По соглашению константы перечисления обычно записывают заглавными буквами, поскольку они представляют постоянные значения.
Обратиться к константе перечисления можно, указав имя класса перечисления, а затем имя константы:
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
fun main() {
// `Direction.NORTH` is an enum constant of type `Direction`.
val direction: Direction = Direction.NORTH
println(direction)
// NORTH
}
Каждый класс перечисления в Kotlin наследуется от базового класса Enum<T>, где T — сам класс перечисления. Например, класс перечисления Direction наследуется от Enum<Direction>. Поэтому у констант перечисления есть встроенные свойства, такие как name и ordinal.
Работа с константами перечисления
Поскольку константы перечисления являются значениями, вы можете присваивать их переменным, выводить на экран, передавать функциям, сравнивать и использовать в выражениях when.
Объявление констант перечисления
Чтобы объявить константы перечисления, сначала определите свойства в конструкторе класса перечисления, а затем передайте значения каждой константе перечисления в круглых скобках. В отличие от некоторых других языков, в Kotlin не используется синтаксис присваивания, например RED = "#FF0000".
Константам перечисления можно сопоставить значения любого типа. Часто используются строки и числа, но можно также использовать другие типы, например Boolean, другой класс перечисления или пользовательский класс.
Рассмотрим класс перечисления Color, в котором для каждого цвета хранится его шестнадцатеричный код:
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
Значение, передаваемое каждой константе перечисления, должно соответствовать типу параметра конструктора. Здесь hex — это строковое свойство класса перечисления Color. Каждая константа перечисления передаёт собственное строковое значение для этого свойства.
Константам перечисления также можно сопоставлять числовые значения. Например, объявите в конструкторе тип Int и укажите значение Int для каждой константы перечисления:
enum class Priority(val level: Int) {
LOW(0),
MEDIUM(1),
HIGH(2)
}
Доступ к константам перечисления и их свойствам
Обратиться к константе перечисления можно через имя класса перечисления. Чтобы обратиться к свойству, связанному с константой перечисления, используйте точечную нотацию, например color.hex или Color.GREEN.hex:
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
fun main() {
val color: Color = Color.RED
println(color)
// RED
println(color.hex)
// #FF0000
println(Color.GREEN.hex)
// #00FF00
}
Здесь Color.RED — это константа перечисления типа Color. Переменная color хранит эту константу перечисления.
Помимо определяемых вами свойств, у каждой константы перечисления есть встроенные свойства name и ordinal, которые позволяют получить её имя и позицию (начиная с 0) в объявлении класса перечисления:
enum class RGB { RED, GREEN, BLUE }
fun main() {
println(RGB.RED.name)
// RED
println(RGB.RED.ordinal)
// 0
}
Передача констант перечисления функциям
Поскольку константы перечисления являются значениями, их можно передавать функциям. Так функция принимает только фиксированный набор вариантов, определённых в классе перечисления, что обеспечивает безопасность типов в коде:
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
//sampleStart
fun printColor(color: Color) {
println("Color: $color")
println("Hex code: ${color.hex}")
}
fun main() {
printColor(Color.BLUE)
// Color: BLUE
// Hex code: #0000FF
}
//sampleEnd
Здесь функция printColor() принимает значение типа Color, поэтому ей можно передать любую константу перечисления Color.
Хотя константы перечисления ведут себя как объекты-одиночки, компилятор рассматривает их как значения типа класса перечисления. Само имя класса перечисления можно использовать как тип, но константы перечисления нельзя использовать в качестве типов перечисления:
enum class Color {
RED, GREEN, BLUE
}
fun printColor(color: Color) {
println(color)
}
fun printRed(color: Color.RED) {
println(color)
// Error: enum entry cannot be used as a type
}
Использование констант перечисления в выражениях when
Классы перечислений лучше всего сочетаются с выражениями when, когда нужно обработать каждую константу отдельно:
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
//sampleStart
fun describeColor(color: Color): String {
return when (color) {
Color.RED -> "Red is a warm color"
Color.GREEN -> "Green is a natural color"
Color.BLUE -> "Blue is a cool color"
}
}
fun main() {
println(describeColor(Color.RED))
// Red is a warm color
}
//sampleEnd
Если в выражении when используются все константы перечисления, ветвь else не нужна.
Поиск констант перечисления
Иногда нужно получить константу перечисления по строке, индексу или одному из связанных с ней значений. В Kotlin есть встроенные API для поиска констант по имени, позиции или пользовательским значениям.
Например, рассмотрим класс перечисления, в котором каждому цвету соответствует значение RGB. Чтобы найти константу перечисления по имени, используйте функцию valueOf():
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
fun main() {
val color = Color.valueOf("RED")
println(color)
// RED
}
Имя, передаваемое в valueOf(), должно точно совпадать с именем константы перечисления. Если константы перечисления с указанным именем нет, valueOf() выбрасывает исключение IllegalArgumentException.
Чтобы найти константу перечисления по её позиции в объявлении перечисления, используйте функцию getOrNull() для свойства entries перечисления:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
//sampleStart
fun main() {
val color = Color.entries.getOrNull(0)
println(color)
// RED
}
//sampleEnd
Отсчёт позиций перечисления начинается с 0. В этом примере RED находится на позиции 0, GREEN — на позиции 1, а BLUE — на позиции 2.
Это полезно, если у вас есть целое число, например полученное из файла или введённое пользователем, которое обозначает позицию константы перечисления. В отличие от некоторых других языков, Kotlin не позволяет напрямую преобразовать Int в константу перечисления. Вместо этого используйте целое число как индекс и найдите константу с помощью entries.getOrNull(index).
Если целое число обозначает значение, которое должно оставаться неизменным даже при изменении порядка констант перечисления, определите явное числовое свойство, например rgb или code, и найдите константу с соответствующим значением.
Поскольку entries — это специализированный List, с ним можно использовать стандартные API коллекций. Например, чтобы найти константу перечисления по связанному значению, выполните поиск по элементам с помощью first():
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
//sampleStart
fun main() {
val color = Color.entries.first { it.rgb == 0xFF0000 }
println(color)
// RED
}
//sampleEnd
Функция first() выбрасывает исключение NoSuchElementException, если соответствующая константа не найдена. Чтобы вместо этого получить null, используйте firstOrNull().
Чтобы получить количество констант перечисления, используйте свойство size. Например:
enum class RGB { RED, GREEN, BLUE }
fun main() {
println(RGB.entries)
// [RED, GREEN, BLUE]
println(RGB.entries.size)
// 3
println("The first color is: ${RGB.valueOf("RED")}")
// "The first color is: RED"
}
Если вам часто нужно искать константы перечисления по имени, позиции или связанному значению, добавьте вспомогательные функции в объект-компаньон:
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF);
companion object {
fun fromName(name: String): Color? =
entries.find { it.name == name }
fun fromPosition(position: Int): Color? =
entries.getOrNull(position)
fun fromRgb(rgb: Int): Color? =
entries.find { it.rgb == rgb }
}
}
fun main() {
println(Color.fromName("RED"))
// RED
println(Color.fromPosition(1))
// GREEN
println(Color.fromRgb(0x0000FF))
// BLUE
println(Color.fromRgb(0xABCDEF))
// null
}
Вспомогательные функции объекта-компаньона полезны, если нужны безопасные операции поиска, возвращающие null вместо выбрасывания исключения.
Использованные выше API поиска, такие как entries и valueOf(), являются примерами синтетических членов. В этом контексте «синтетический» означает, что Kotlin автоматически предоставляет эти члены, хотя вы не объявляете их самостоятельно. Поэтому в любом классе перечисления можно получить список его констант с помощью свойства entries и получить константу по имени с помощью функции valueOf(), не написав дополнительного кода.
Получить доступ к константам класса перечисления можно с помощью универсальных вспомогательных функций, таких как enumEntries<T>() и enumValueOf<T>(). Эти функции используют параметры типа с модификатором reified. Такие параметры сохраняют фактический тип перечисления внутри обобщённой inline-функции, поэтому вспомогательные функции могут работать непосредственно с типом перечисления T:
Функция |
Описание |
|---|---|
(Рекомендуется) Возвращает все элементы перечисления типа |
|
Возвращает массив со всеми элементами перечисления типа |
|
Возвращает один элемент перечисления по его имени и выбрасывает исключение |
Например:
import kotlin.enums.enumEntries
enum class RGB { RED, GREEN, BLUE }
inline fun <reified T : Enum<T>> printAllValues() {
println(enumEntries<T>().joinToString { it.name })
}
inline fun <reified T : Enum<T>> findByName(name: String): T = enumValueOf<T>(name)
fun main() {
printAllValues<RGB>()
// RED, GREEN, BLUE
println(findByName<RGB>("GREEN"))
// GREEN
}
Подробнее об inline-функциях и параметрах типа с модификатором reified см. в разделе Inline-функции.
Сравнение и сортировка констант перечисления
Для сравнения констант перечисления используйте оператор == структурного равенства:
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF")
}
//sampleStart
fun main() {
val color = Color.RED
println(color == Color.RED)
// true
println(color == Color.BLUE)
// false
}
//sampleEnd
Поскольку каждая константа перечисления ведёт себя как объект-одиночка, при сравнении констант проверяется, ссылаются ли оба значения на одну и ту же константу.
Все классы перечисления по умолчанию реализуют интерфейс Comparable, поэтому константы перечисления можно сравнивать и сортировать. Порядок констант определяется их позицией в объявлении перечисления (значением ordinal), поэтому первой объявленной константой считается наименьшая:
enum class Priority {
LOW, MEDIUM, HIGH
}
fun main() {
println(Priority.LOW < Priority.HIGH)
// true
println(Priority.HIGH > Priority.MEDIUM)
// true
}
Сортировка выполняется в том же порядке, в котором константы объявлены. Например, entries.sorted() возвращает константы в порядке объявления, независимо от их имён:
enum class Priority {
HIGH, LOW, MEDIUM
}
fun main() {
println(Priority.entries.sorted())
// [HIGH, LOW, MEDIUM]
}
Класс Enum<T> предоставляет функции compareTo(), equals() и hashCode(), поведение которых нельзя переопределить, как в обычных классах. Сравнение всегда выполняется в порядке объявления.
Если нужен другой порядок, не полагайтесь на порядок объявления. Вместо этого определите явное свойство и сортируйте по нему. Например, отсортируйте цвета по яркости:
enum class Color(val brightness: Int) {
RED(1),
GREEN(3),
BLUE(2)
}
fun main() {
println(Color.entries.sortedBy { it.brightness })
// [RED, BLUE, GREEN]
}
Подробнее см. в разделе Порядок элементов.
Добавление функций в классы перечислений
Как и свойства, классы перечислений могут содержать функции. Можно добавлять функции, общие для всех констант перечисления, сочетать их со свойствами или определять операторные функции.
Добавление функций, общих для всех констант
Чтобы добавить поведение, общее для всех констант перечисления, определите функцию в теле класса перечисления. Если класс перечисления содержит члены, отделите определения констант от определений членов точкой с запятой:
enum class Direction {
NORTH, SOUTH, WEST, EAST;
fun isVertical(): Boolean = this == NORTH || this == SOUTH
}
fun main() {
println(Direction.NORTH.isVertical())
// true
println(Direction.EAST.isVertical())
// false
}
Каждая константа перечисления может вызывать общую функцию. Внутри функции this обозначает константу перечисления, для которой она вызвана.
Можно сочетать свойства конструктора с функциями, чтобы связать данные с каждой константой и добавить поведение, использующее эти данные:
enum class Color(val hex: String) {
RED("#FF0000"),
GREEN("#00FF00"),
BLUE("#0000FF");
fun describe(): String = "$name has hex code $hex"
}
fun main() {
println(Color.RED.describe())
// RED has hex code #FF0000
}
Здесь каждая константа хранит собственное значение hex, а общая функция describe() использует как встроенное свойство name, так и свойство hex.
Добавление операторных функций
В классах перечислений также можно определять операторные функции, чтобы использовать константы перечисления с операторами. Например, определите операторную функцию not(), которая возвращает противоположное направление при использовании оператора !:
enum class Direction {
NORTH, SOUTH, WEST, EAST;
operator fun not(): Direction = when (this) {
NORTH -> SOUTH
SOUTH -> NORTH
WEST -> EAST
EAST -> WEST
}
}
fun main() {
println(!Direction.NORTH)
// SOUTH
}
Использование анонимных классов
Для констант перечисления можно объявлять собственные анонимные классы с соответствующими функциями, а также переопределять функции базового класса. При использовании анонимных классов тело класса указывается непосредственно после имени константы перечисления, и Kotlin выводит класс перечисления в качестве супертипа.
Это полезно, если в классе перечисления объявлена абстрактная функция и каждая константа должна предоставить собственную реализацию. Каждая константа переопределяет абстрактную функцию в своём анонимном классе:
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
fun main() {
var state = ProtocolState.WAITING
println(state)
// WAITING
state = state.signal()
println(state)
// TALKING
}
Здесь каждая константа по-разному реализует абстрактную функцию signal(), поэтому вызов signal() возвращает следующее состояние в зависимости от константы.
Хотя константы перечисления ведут себя как объекты-одиночки, тип константы перечисления — это сам класс перечисления, а не её собственный анонимный класс. Поэтому нельзя обращаться к членам, объявленным внутри тела анонимного класса:
enum class ProtocolState {
WAITING {
val waitingMessage = "Waiting for a signal"
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
fun main() {
println(ProtocolState.WAITING.waitingMessage)
// Error: unresolved reference 'waitingMessage'
}
Чтобы предоставить данные или поведение для каждой константы, объявите их в теле класса перечисления и используйте абстрактный член, если для каждой константы нужна собственная реализация.
Реализация интерфейсов в классах перечислений
Класс перечисления может реализовать интерфейс, но не может наследоваться от класса. Можно предоставить общую реализацию членов интерфейса для всех констант перечисления или позволить каждой константе предоставить собственную реализацию в анонимном классе.
Чтобы реализовать интерфейс, добавьте его в объявление класса перечисления:
import java.util.function.BinaryOperator
import java.util.function.IntBinaryOperator
//sampleStart
enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
PLUS {
override fun apply(t: Int, u: Int): Int = t + u
},
TIMES {
override fun apply(t: Int, u: Int): Int = t * u
};
override fun applyAsInt(t: Int, u: Int) = apply(t, u)
}
//sampleEnd
fun main() {
val a = 13
val b = 31
for (f in IntArithmetics.entries) {
println("$f($a, $b) = ${f.apply(a, b)}")
}
}
В этом примере класс перечисления IntArithmetics реализует два интерфейса, указанных в объявлении класса перечисления: BinaryOperator<Int> и IntBinaryOperator. Каждая константа может переопределить члены интерфейса в теле собственного анонимного класса, как это делают PLUS и TIMES для apply(), а applyAsInt() предоставляет общую реализацию для всех констант.
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/enum-classes.html