На этом шаге мы рассмотрим назначение и использование этих конструкций.
Ваш сундук может содержать награду любого вида, но только одну. А что, если понадобится сохранить несколько наград 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.