Перейти до вмісту

Освоюємо Kotlin/Enum класи

Матеріал з Вікіпідручника


Enum класи в Kotlin - це спеціальні класи для створення переліків (від англ. enumaration).

Простий перелік

[ред.]

Перелік видів старовинної зброї може виглядати так:

enum class Weapon {
    SWORD,
    AXE,
    BOW
}

Кожен елемент переліку є константою і об'єктом, розмежовуються елементи при переліченні комами.

Ініціалізація констант

[ред.]

Перелік може мати конструктор і ініціалізувати певні властивості для кожного елементу переліку. Наприклад додамо значення шкоди для кожного виду зброї:

enum class Weapon (var damage:Int){
    SWORD (15),
    AXE (20),
    BOW (16)
}
fun main(){
    val weapon: Weapon = Weapon.AXE
    println(weapon.damage)        // 20
    println(Weapon.BOW.damage) // 16
}

При потребі можна додавати кілька властивостей.

Визначення функцій у Enum класах

[ред.]

Enum класи можуть містити також функції. Такі функції визначаються після переліку і крапки з комою:

enum class Weapon (var damage:Int){
    SWORD (15),
    AXE (20),
    BOW (16);
    fun getDescription():String{
        return "Зброя:$this шкода:$damage"
    }
}

fun main(){
    val weapon=Weapon.AXE
    println(weapon.getDescription())
}
Standard input or output Результат:
Зброя:AXE шкода:20

Властивості та функції Enum класів

[ред.]

Всі переліки володіють двома властивостями name та ordinary. Перша властивість повертає ім'я переліку, друга порядковий номер переліку починаючи з нуля.

enum class Weapon (var damage:Int){
    SWORD (15),
    AXE (20),
    BOW (16)
}
fun main(){
    val weapon1=Weapon.AXE
    println(weapon1.name)        // AXE
    println(weapon1.ordinal)     // 1
}

Також, в Kotlin для Enum класу є додаткові функції:

  • valueOf(value: String) - повертає об'єкт переліку по назві константи
  • values() - повертає масив констант переліку

Замість функції values() можна скористатись властивістю EnumClass.entries: EnumEntries<EnumClass> // спеціалізований List<EnumClass>

enum class Weapon (var damage:Int){
    SWORD (15),
    AXE (20),
    BOW (16)
}
fun main() {

    for(weapon in Weapon.values())
        println("values(): "+weapon)
    for (weapon in Weapon.entries)
        println("Entries: "+weapon.toString())
    val weapon2:Weapon=Weapon.valueOf("BOW")
    println("valueOf(): "+weapon2)
}
Standard input or output Результат:
values(): SWORD
values(): AXE
values(): BOW
Entries: SWORD
Entries: AXE
Entries: BOW
valueOf(): BOW

Функція valueOf() викидає виняток IllegalArgumentException, якщо вказана назва не співпадає з константами переліку.

Анонімні класи

[ред.]

Enum константи можуть декларувати свої власні анонімні класи із відповідними функціями та властивостями або заміщати (override) методи і властивості Enum класу:

enum class Weapon (){
    SWORD{
        override val damage = 20
        override fun getUkrName(): String {
            return "меч"
        }
    },
    AXE{
        override val damage = 25
        override fun getUkrName(): String {
            return "сокира"
        }
    },
    BOW{
        override val damage = 11
        override fun getUkrName(): String {
            return "лук"
        }
    };

    abstract fun getUkrName():String
    abstract val damage:Int
}
fun main() {
    val weapon1 = Weapon.AXE
    val weapon2 = Weapon.BOW
    println("Зброя: ${weapon1.getUkrName()}, шкода: ${weapon1.damage}")
    println("Зброя: ${weapon2.getUkrName()}, шкода: ${weapon2.damage}")
}
Standard input or output Результат:
Зброя: сокира, шкода: 25
Зброя: лук, шкода: 11

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

[ред.]

Enum класи також можуть реалізовувати інтерфейси:

interface Printable {
    fun printInfo()
}

enum class Weapon : Printable {
    SWORD {
        override fun printInfo() {
            println("меч")
        }
    },
    AXE {
        override fun printInfo() {
            println("сокира")
        }
    },
    BOW {
        override fun printInfo() {
            println("сокира")
        }
    }
}

fun main() {
    Weapon.AXE.printInfo()
}

Приклади програм із застосуванням переліку

[ред.]

Наступна програма створює двох солдат, які б'ються між собою (почергову наносять собі удари) поки в одного з них не закінчиться здоров'я. При цьому кожен солдат отримує при створенні зброю із списку переліку Weapon.

//перелік зброї
enum class Weapon (val damage:Int, val ukrName:String){
    SWORD(16, "меч"),
    AXE(20, "сокира"),
    BOW(14, "лук")
}

//клас солдата
class Soldier(var name:String, var health:Int, val weapon:Weapon)

//функція, яка реалізовує удар одного солдата іншим,
//віднімається здоров'я у того солдата, якого вдарили
fun hit(soldier: Soldier, hittedSoldier: Soldier){
    println (soldier.name+" вдарив "+hittedSoldier.name)
    hittedSoldier.health=hittedSoldier.health-soldier.weapon.damage
    if (hittedSoldier.health<=0) println(hittedSoldier.name+" помер")
}
fun main() {
    val soldier1=Soldier("Adam", 100, Weapon.AXE)
    val soldier2=Soldier("Ren", 100, Weapon.SWORD)

    //солдати б'ються почергово до смерті одного з них
    while ((soldier1.health>0)&&(soldier2.health>0)){
        hit (soldier1, soldier2)
        if (soldier2.health>0)hit (soldier2, soldier1)
    }
}
Standard input or output Результат:
Adam вдарив Ren
Ren вдарив Adam
Adam вдарив Ren
Ren вдарив Adam
Adam вдарив Ren
Ren вдарив Adam
Adam вдарив Ren
Ren вдарив Adam
Adam вдарив Ren
Ren помер
Note Ви можете модифікувати бій між солдатами, таким чином, щоб солдати отримували шкоду випадковим числом від 0 до максимальної шкоди зброї. Таким чином вивід програми стане цікавіший і не обов'язково помре другий солдат. Також, в ідеалі функція hit() не мала б напряму звертатися до властивостей солдата. Властивості солдата мали б бути приватними і доступ до них мав би бути за допомогою відповідних функцій самого солдата, згідно принципу інкапсуляції.

Часто необхідно зробити певну дію в програмі, в залежності від певної константи переліку. Тоді можна перебрати константи з допомогою конструкції when. Наступний приклад демонструє присвоєння класу солдату (піхотинець або лучник) в залежності від його зброї:

enum class Weapon (val damage:Int, val ukrName:String){
    SWORD(16, "меч"),
    AXE(20, "сокира"),
    BOW(14, "лук")
}

class Soldier(var name:String, var health:Int, val weapon:Weapon){
    fun getClass():String{
        return when(weapon){
            Weapon.SWORD->"піхотинець"
            Weapon.AXE->"піхотинець"
            Weapon.BOW->"лучник"
        }
    }
}

fun main() {
    val soldier1=Soldier("Adam", 100, Weapon.AXE)
    val soldier2=Soldier("Ren", 100, Weapon.SWORD)
    println("${soldier1.name} - ${soldier1.getClass()}")
    println("${soldier2.name} - ${soldier2.getClass()}")
}
Standard input or output Результат:
Adam - піхотинець
Ren - піхотинець

В конструкції when необхідно перерахувати усі можливі випадки переліку або застосувати else.