kotlin 泛型:无法推断类型参数

Gui*_*ume 5 generics kotlin

我需要 Kotlin 中的一个集合来仅包含实现给定接口的元素。

例如:包含动物集合的地图:

interface Animal { val name: String }
data class Monkey(override val name: String): Animal
data class Snake(override val name: String): Animal
Run Code Online (Sandbox Code Playgroud)

从阅读的文档和博客做题,我写了这个代码,使用泛型关键字:

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")))
    }        
}
Run Code Online (Sandbox Code Playgroud)

现在我想在 Test 类中添加一个方法来读取“数据”的内容,例如将其打印到控制台:

fun printAll() {
   data.forEach { collectionName: String, animals: ArrayList<in Animal> -> 
       println(collectionName)
       animals.forEach { animal: Animal ->
           println("\t$animal")
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我这样做,我有一个编译错误:

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)
Run Code Online (Sandbox Code Playgroud)

我的解决方案是强制我的动物成为一个 ArrayList< out Animal>:

...
(animals as ArrayList<out Animal>).forEach { animal: Animal ->
    println("\t$animal")
}
...
Run Code Online (Sandbox Code Playgroud)

但我不确定这是编写此类代码的最佳方式。有没有更好的方法告诉 Kotlin 我想在泛型中为生产者和消费者使用子类型?

hot*_*key 4

我想您不需要in类型中的关键字data

此处使用in意味着您希望这些 s 的类型参数至少与 一样通用ArrayList,这意味着 an实际上也可以使用 的超类型进行参数化:您甚至可以将 an 放入映射中,这清楚地表明它不是类型安全,期望列表仅包含s。AnimalArrayList<in Animal>AnimalArrayList<Any>Animal

考虑删除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")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)