Мне нужна коллекция в Котлине, чтобы она содержала только элементы, реализующие данный интерфейс.
Например: карта с коллекцией животных:
interface Animal { val name: String }
data class Monkey(override val name: String): Animal
data class Snake(override val name: String): Animal
Из прочтения документации, блогов и ТАК я написал этот код, в котором используется ключевое слово Generics in :
class Test {
private val data = HashMap<String, ArrayList<in Animal>>()
init {
data.put("Monkeys", arrayListOf(Monkey("Kong"), Monkey("Cheetah")))
data.put("Snakes", arrayListOf(Snake("Monthy"), Snake("Kaa")))
}
}
Теперь я хочу добавить метод в класс Test, который читает содержимое «данных», например, чтобы распечатать его на консоли:
fun printAll() {
data.forEach { collectionName: String, animals: ArrayList<in Animal> ->
println(collectionName)
animals.forEach { animal: Animal ->
println("\t$animal")
}
}
}
Если я это сделаю, у меня будет ошибка компиляции:
Error:(27, 21) Kotlin: Type inference failed: Cannot infer type parameter T in inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit
None of the following substitutions
receiver: Iterable<Any?> arguments: ((Any?) -> Unit)
receiver: Iterable<Animal> arguments: ((Animal) -> Unit)
can be applied to
receiver: kotlin.collections.ArrayList<in Animal> /* = java.util.ArrayList<in Animal> */ arguments: ((Animal) -> Unit)
Мое решение состоит в том, чтобы принудить моего животного к списку ArrayList <<strong> out Animal>:
...
(animals as ArrayList<out Animal>).forEach { animal: Animal ->
println("\t$animal")
}
...
Но я не уверен, что это лучший способ написания такого рода кода.
Есть ли лучший способ сообщить Kotlin, что я хочу использовать подтипы в дженериках как для производителей, так и для потребителей?