首页 文章

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

提问于
浏览
3

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

例如:包含动物集合的 Map :

interface Animal { val name: String }
data class Monkey(override val name: String): Animal
data class Snake(override val name: String): Animal

通过阅读文档和博客以及SO问题,我编写了使用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类中添加一个读取'data'内容的方法,例如将其打印到控制台:

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 < out Animal>:

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

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

1 回答

  • 4

    我想你不需要 data 类型的 in 关键字 .

    在这里使用 in 意味着你希望那些 ArrayList 的类型参数至少和 Animal 一样通用,这意味着 ArrayList<in Animal> 实际上也可以使用超类型 Animal 进行参数化:你甚至可以将 ArrayList<Any> 放入 Map 中,明确表示期望列表仅保留 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")
            }
        }
    }
    

相关问题