【问题标题】:kotlin generics: Cannot infer type parameterkotlin 泛型:无法推断类型参数
【发布时间】:2018-05-07 13:06:02
【问题描述】:

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

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

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

通过阅读文档和博客以及 SO 问题,我编写了使用泛型 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)

我的解决方案是强制我的动物使用 ArrayListout Animal>:

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

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

【问题讨论】:

    标签: generics kotlin


    【解决方案1】:

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

    在这里使用in 意味着您希望那些ArrayLists 的类型参数至少和Animal 一样通用,这意味着ArrayList&lt;in Animal&gt; 实际上可以用参数化Animal 的超类型:您甚至可以将 ArrayList&lt;Any&gt; 放入映射中,这清楚地表明期望列表仅包含 Animals 是不安全的。

    考虑删除in 关键字,只留下ArrayList&lt;Animal&gt;(甚至是List&lt;Animal&gt;,这是只读列表的接口):

    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")
            }
        }
    }
    

    【讨论】:

    • 确实,切换到不可变的Lists 有一个额外的好处:这意味着现在可以将listOf(Monkey("Kong"), Monkey("Cheetah")) 视为List&lt;Monkey&gt; 而不是List&lt;Animal&gt;
    • 这个解决方案也适用于 MutableList 接口,如果我需要一个检索列表的函数来修改它,这很方便。
    • 在类签名中看到in/out 泛型关键字来定义类是“使用”还是“产生”该类型更为常见。您可以在此处阅读有关这些关键字的信息:kotlinlang.org/docs/reference/…
    猜你喜欢
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2018-05-15
    • 2023-03-08
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    相关资源
    最近更新 更多