如何以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是Ref
。 filterIsInstance
失败,因为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
。 不受控制的演员不幸是不可避免的。