Шаг 204.
Основы Kotlin.
Обобщения. Конструкции vararg и get

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

    Ваш сундук может содержать награду любого вида, но только одну. А что, если понадобится сохранить несколько наград Loot в LootBox?

    Для этого измените главный конструктор LootBox, добавив ключевое слово vararg, которое позволяет передавать произвольное количество аргументов в конструктор.

class LootBox<T : Loot>(vararg item: T) {
    .   .   .   .
}


Рис.1. Добавление vararg (Generics.kt)

    Теперь, когда вы добавили ключевое слово vararg в LootBox, его переменная item будет интерпретироваться компилятором как массив элементов, а не как один элемент, что позволит конструктору LootBox принять несколько наград.

    Обновите переменную loot и функцию fetch(), чтобы учесть это изменение, добавив выборку наград из массива loot по индексу.

class LootBox<T>(vararg item: T) {
    var open = false
    private var loot: Array<out T> = item

    fun fetch(item: Int): T? {
        return loot[item].takeIf { open }
    }

    fun <R> fetch(item: Int, lootModFunction: (T) -> R): R? {
        return lootModFunction(loot[item]).takeIf { open }
    }

}


Рис.2. Индексирование массива loot (Generics.kt)

    Обратите внимание на ключевое слово out, добавленное в новое объявление типа переменной loot. Ключевое слово out необходимо, потому что оно является частью возвращаемого типа любой переменной, объявленной как vararg. С этим ключевым словом и его "напарником" in вы прямо сейчас и познакомитесь.

    Попробуйте новую улучшенную версию LootBox в main(). Положите еще одну шляпу в сундук (если хотите, то проявите изобретательность, придумав имя для второй шляпы). Затем извлеките две награды из lootBoxOne, каждую своим вызовом fetch().

.   .   .   .
fun main(args: Array<String>) {
    val lootBoxOne: LootBox<Fedora> = LootBox(
            Fedora("a generic-looking fedora", 15), 
            Fedora("a dazzling magenta fedora", 25)))
    val lootBoxTwo: LootBox<Coin> = LootBox(Coin(15))

    lootBoxOne.fetch(1)?.run {
        println("You retrieve $name from the box!")
    }

    val coin = lootBoxOne.fetch(0) {
        Coin(it.value * 3)
    }
    coin?.let { println(it.value) }

}


Рис.3. Тестирование LootBox (Generics.kt)

    Запустите Generics.kt снова. Вы увидите название второй награды в lootBoxOne и ценность первой награды в монетах:

You retrieve a dazzling magenta fedora from the box!
45


Рис.4. Результат работы приложения

    Еще один способ обеспечить доступ по индексу к массиву loot - реализовать в LootBox функцию get(), которая позволяет использовать оператор [ ].

    Обновите LootBox, добавив реализацию get().

class LootBox<T>(vararg item: T) {
    var open = false
    private var loot: Array<out T> = item

    operator fun get(index: Int): T? = loot[index].takeIf { open }

    fun fetch(item: Int): T? {
        return loot[item].takeIf { open }
    }

    fun <R> fetch(item: Int, lootModFunction: (T) -> R): R? {
        return lootModFunction(loot[item]).takeIf { open }
    }

}


Рис.5. Добавление оператора get() в LootBox (Generics.kt)

    Теперь воспользуйтесь новым оператором get() в функции main().

.   .   .   .
fun main(args: Array<String>) {
    val lootBoxOne: LootBox<Fedora> = LootBox(
            Fedora("a generic-looking fedora", 15), 
            Fedora("a dazzling magenta fedora", 25)))
    val lootBoxTwo: LootBox<Coin> = LootBox(Coin(15))

    lootBoxOne.fetch(1)?.run {
        println("You retrieve $name from the box!")
    }

    val coin = lootBoxOne.fetch(0) {
        Coin(it.value * 3)
    }
    coin?.let { println(it.value) }

    val fedora = lootBoxOne[1]
    fedora?.let { println(it.name) }
}
Файл с проектом можно взять здесь.


Рис.6. Использование get() (Generics.kt)

    Оператор get() позволяет быстро забрать награду по указанному индексу. Запустите Generics.kt снова, и вы увидите название второй награды Fedora из lootBoxOne, следующее за предыдущим сообщением:

You retrieve a dazzling magenta fedora from the box!
45
a dazzling magenta fedora


Рис.7. Результат работы приложения

    На следующем шаге мы рассмотрим конструкции in и out.




Предыдущий шаг Содержание Следующий шаг