如何以types安全的方式从通用列表中检索项目

我想把项目放在一个通用的容器中,但以types安全的方式检索它们。 容器在添加时会为每个项目添加一个序列号。 我试图在Kotlin中实现这个,但在pens()方法中遇到了问题。 有没有办法在函数参数中使用types信息来定义返回值的types?

 import java.util.concurrent.atomic.AtomicInteger import kotlin.reflect.KClass interface Item data class Pen(val name: String) : Item data class Eraser(val name: String) : Item data class Ref (val id: Int, val item: T) class Container { private val seq = AtomicInteger(0) private val items: MutableList<Ref> = mutableListOf() fun add(item: T) = items.add(Ref(seq.incrementAndGet(), item)) fun filter(cls: KClass) : List<Ref> = items.filter{cls.isInstance(it.item)}.toList() // to retrieve pens in a type safe manner // Type mismatch required: List<Ref> found: List<Ref> fun pens(): List<Ref> = filter(Pen::class) } fun main(args: Array) { val container = Container() container.add(Pen("Pen-1")) container.add(Pen("Pen-2")) container.add(Eraser("Eraser-3")) container.add(Eraser("Eraser-4")) // the filter method works container.filter(Pen::class).forEach{println(it)} } 

我通过将通用参数移动到方法中并进行不安全的转换,在filter操作之后应该总是成功。 这是完整的代码。

 import java.util.concurrent.atomic.AtomicInteger import kotlin.reflect.KClass interface Item data class Pen(val name: String) : Item data class Eraser(val name: String) : Item data class Ref (val id: Int, val item: T) class Container { private val seq = AtomicInteger(0) private val items: MutableList> = mutableListOf() fun  add(item: T) = items.add(Ref(seq.incrementAndGet(), item)) private fun  filter(cls: KClass) : List> = items.filter{cls.isInstance(it.item)}.map{it as Ref} fun pens(): List> = filter(Pen::class) fun erasers(): List> = filter(Eraser::class) } fun main(args: Array) { val container = Container() container.add(Pen("Pen-1")) container.add(Eraser("Eraser-2")) container.add(Pen("Pen-3")) container.add(Eraser("Eraser-4")) container.pens().forEach{println(it)} container.erasers().forEach{println(it)} } 

您的filter方法返回typesList>而您的pens方法返回typesList> 。 由于您将container声明为Container ,因此filtertypes实际上是List> 。 您不能将List>设置为等于List>

你可以让pens返回List> (这可能会破坏你正在寻找的types安全)。 或者,对于pens ,将列表转换为List> 。 例如…

 fun pens(): List> = filter(Pen::class).map { it as Ref ) } 

根据Kotlin文档 ,上面使用的as操作符是一个“不安全”的操作符。 这是因为it.item可能是item任何item或子types。 如果it.item不是一个Pen ,就会抛出一个exception。 但是,由于您只筛选Pen项目,它永远不会抛出exception。

你问:

有没有办法在函数参数中使用types信息来定义返回值的types?

是。 您可以使用通用函数和通用 types参数来完成此操作。 你得到它使用一个通用的function。 有关使用参数化的示例,请参阅下面的示例。

讨论

不幸的是, filter只返回原始types的项目:

 fun  Iterable.filter(predicate: (T) -> Boolean): List 

这意味着你必须使用一个filter并返回一个不同types的方法。 尝试使用诸如filterIsInstance的方法来完成这项工作是很有诱惑力的。 不幸的是,在这种情况下,types是ReffilterIsInstance失败,因为T变成擦除types,导致所有项目被返回。 这意味着你必须定义你自己的版本,它基于成员variablestypes而不是整个types进行测试。 我的首选是使这个列表的function,而不是容器,如下所示:

 import java.util.concurrent.atomic.AtomicInteger import kotlin.reflect.KClass interface Item data class Pen(val name: String) : Item data class Eraser(val name: String) : Item data class Ref (val id: Int, val item: T) inline fun  List>.filter(predicate: (Ref<*>) -> Boolean): List> { val result = ArrayList>() @Suppress("UNCHECKED_CAST") for(item in this) if (predicate(item)) result.add(item as Ref) return result } class Container { private val seq = AtomicInteger(0) private val items: MutableList> = mutableListOf() fun add(item: T) = items.add(Ref(seq.incrementAndGet(), item)) // to retrieve pens in a type safe manner fun pens() = items.filter { it.item is Pen } } fun main(args: Array) { val container = Container() container.add(Pen("Pen-1")) container.add(Pen("Pen-2")) container.add(Eraser("Eraser-3")) container.add(Eraser("Eraser-4")) println("[${container.pens()}]") } 

通过最小的努力,这种方法允许您在代码中的任何地方执行类似的过滤,而且它是惯用的Kotlin。

内联函数是filterIsInstance的Kotlin源代码的filterIsInstance 。 不受控制的演员不幸是不可避免的。