代码之家  ›  专栏  ›  技术社区  ›  Guillaume

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

  •  5
  • Guillaume  · 技术社区  · 6 年前

    我需要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")))
        }        
    }
    

    现在,我想在测试类中添加一个读取“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< 出来 动物(>):

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

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

    1 回复  |  直到 6 年前
        1
  •  4
  •   hotkey    6 年前

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

    使用 在里面 这里意味着您需要 ArrayList s将成为 至少与一般情况一样 Animal ,意味着 ArrayList<in Animal> 实际上可以用的超类型参数化 动物 还有:你甚至可以 ArrayList<Any> 这清楚地表明,期望列表只保存是不安全的 动物 s

    考虑删除 在里面 关键字和离开 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")
            }
        }
    }