Обобщения kotlin: невозможно определить тип параметра - PullRequest
0 голосов
/ 07 мая 2018

Мне нужна коллекция в Котлине, чтобы она содержала только элементы, реализующие данный интерфейс.

Например: карта с коллекцией животных:

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, что я хочу использовать подтипы в дженериках как для производителей, так и для потребителей?

1 Ответ

0 голосов
/ 07 мая 2018

Полагаю, вам не нужно ключевое слово in в типе data.

Использование здесь in означает, что вы хотите, чтобы аргументы типа этих ArrayList s были по крайней мере, в общем как Animal, что означает, что ArrayList<in Animal> может быть на самом деле параметризован также с супертипом Animal: вы даже можете поместить ArrayList<Any> в карту, что ясночто небезопасно ожидать, что списки будут содержать только Animal s.

Попробуйте удалить ключевое слово in и оставить только ArrayList<Animal> (или даже List<Animal>, который является интерфейсом длясписок только для чтения):

private val data = HashMap<String, List<Animal>>()

init {
    data.put("Monkeys", listOf(Monkey("Kong"), Monkey("Cheetah")))
    data.put("Snakes", listOf(Snake("Monthy"), Snake("Kaa")))
}

fun printAll() {
    data.forEach { collectionName: String, animals: List<Animal> ->
        println(collectionName)
        animals.forEach { animal: Animal ->
            println("\t$animal")
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...