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

Generics kotlin泛型:无法推断类型参数,generics,kotlin,Generics,Kotlin,我需要Kotlin中的集合只包含实现给定接口的元素 例如:包含动物集合的地图: interface Animal { val name: String } data class Monkey(override val name: String): Animal data class Snake(override val name: String): Animal 通过阅读文档和博客等问题,我编写了以下代码,其中使用了关键字中的泛型: class Test { private val d

我需要Kotlin中的集合只包含实现给定接口的元素

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

interface Animal { val name: String }
data class Monkey(override val name: String): Animal
data class Snake(override val name: String): Animal
通过阅读文档和博客等问题,我编写了以下代码,其中使用了关键字中的泛型

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")))
    }        
}
类测试{
private val data=HashMap()
初始化{
数据。put(“猴子”)、arrayListOf(猴子(“香港”)、猴子(“猎豹”))
data.put(“蛇类”)、arrayListOf(蛇类(“Monthy”)、蛇类(“Kaa”))
}        
}
现在,我想在测试类中添加一个读取“数据”内容的方法,例如将其打印到控制台:

fun printAll() {
   data.forEach { collectionName: String, animals: ArrayList<in Animal> -> 
       println(collectionName)
       animals.forEach { animal: Animal ->
           println("\t$animal")
       }
    }
}
fun printAll(){
data.forEach{collectionName:String,animals:ArrayList->
println(集合名称)
animals.forEach{动物:动物->
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)
错误:(27,21)Kotlin:类型推断失败:无法推断内联fun Iterable中的类型参数T。forEach(操作:(T)->Unit):Unit
以下替代品均不适用
接收者:Iterable参数:((任何?->单位)
接收者:Iterable参数:((动物)->单位)
适用于
接收方:kotlin.collections.ArrayList/*=java.util.ArrayList*/参数:((动物)->单位)
我的解决方案是强迫我的动物进入ArrayList:

。。。
(作为ArrayList的动物)。forEach{动物:动物->
println(“\t$animal”)
}
...
但我不确定这是否是编写此类代码的最佳方式。
有没有更好的方法告诉Kotlin我想在泛型中为生产者和消费者使用子类型?

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

在这里使用
意味着您希望那些
ArrayList
的类型参数至少与
Animal
一样通用,这意味着
ArrayList
实际上也可以用
Animal
的超类型参数化:您甚至可以将
ArrayList
放入映射中,这清楚地表明,期望列表只包含
Animal
s是不安全的

考虑删除
关键字中的
,只留下
ArrayList
(甚至是
列表
,它是只读列表的接口):

private val data=HashMap()
初始化{
数据。put(“猴子”)、列表(猴子(“香港”)、猴子(“猎豹”))
数据。输入(“蛇”,列表(蛇(“蒙蒂”),蛇(“Kaa”))
}
趣味打印全部(){
data.forEach{collectionName:字符串,动物:列表->
println(集合名称)
animals.forEach{动物:动物->
println(“\t$animal”)
}
}
}

事实上,切换到不可变的
列表
s还有一个额外的好处:这意味着现在可以将
listOf(Monkey(“Kong”)、Monkey(“Cheetah”)
视为
列表
而不是
列表
。这个解决方案也适用于可变列表界面,如果我需要一个函数来检索列表以对其进行修改,它会很方便。更常见的是在类签名中看到
in
/
out
通用关键字来定义类是“消费”还是“生产”该类型。您可以在此处阅读这些关键字:
...
(animals as ArrayList<out Animal>).forEach { animal: Animal ->
    println("\t$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")
        }
    }
}