首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何以类型安全的方式从泛型列表中检索项

如何以类型安全的方式从泛型列表中检索项
EN

Stack Overflow用户
提问于 2017-08-16 17:37:11
回答 3查看 1.2K关注 0票数 3

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

代码语言:javascript
复制
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<out T : Item> (val id: Int, val item: T)

class Container<T: Item> {
    private val seq = AtomicInteger(0)
    private val items: MutableList<Ref<T>> = mutableListOf()
    fun add(item: T) =  items.add(Ref(seq.incrementAndGet(), item))

    fun filter(cls: KClass<*>) : List<Ref<T>> = items.filter{cls.isInstance(it.item)}.toList()

    // to retrieve pens in a type safe manner
    // Type mismatch required: List<Ref<Pen>> found: List<Ref<T>> 
    fun pens(): List<Ref<Pen>> = filter(Pen::class) 
}

fun main(args: Array<String>) {
    val container = Container<Item>()
    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)}
}
EN

回答 3

Stack Overflow用户

发布于 2017-08-30 05:45:31

你问:

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

是。您可以使用generic functionsreified type parameters来完成此任务。你使用一个泛型函数让它工作。使用实例化参数的示例请参见下面的示例。

讨论

不幸的是,filter只返回原始类型的项:

代码语言:javascript
复制
fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

这意味着您必须使用过滤并返回不同类型的方法。尝试使用像filterIsInstance这样的方法来完成这项工作是很有诱惑力的。不幸的是,在本例中,类型是Ref<T>filterIsInstance失败,因为T变为擦除类型,导致所有项都被返回。这意味着您必须定义自己的版本,该版本基于成员变量类型而不是整个类型进行测试。我更喜欢将其作为列表而不是容器的函数,如下所示:

代码语言:javascript
复制
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<T : Item> (val id: Int, val item: T)

inline fun <reified R: Item> List<Ref<*>>.filter(predicate: (Ref<*>) -> Boolean): List<Ref<R>> {
  val result = ArrayList<Ref<R>>()
  @Suppress("UNCHECKED_CAST")
  for(item in this) if (predicate(item)) result.add(item as Ref<R>)
  return result
}


class Container<T: Item> {
    private val seq = AtomicInteger(0)
    private val items: MutableList<Ref<T>> = mutableListOf()
    fun add(item: T) =  items.add(Ref(seq.incrementAndGet(), item))

    // to retrieve pens in a type safe manner
    fun pens() = items.filter<Pen> { it.item is Pen }
}


fun main(args: Array<String>) {
    val container = Container<Item>()
    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源代码的变体。不幸的是,未经检查的演员阵容是不可避免的。

票数 1
EN

Stack Overflow用户

发布于 2017-08-16 19:04:13

filter方法返回类型List<Ref<T>>,而pens方法返回类型List<Ref<Pen>>。因为您将container声明为Container<Item>,所以过滤器类型实际上是List<Ref<Item>>。不能将List<Ref<Pen>>设置为等于List<Ref<Item>>

您可以让pens返回List<Ref<T>> (这可能会破坏您正在寻找的类型安全性)。或者,对于pens,将带有map的列表转换为List<Ref<Pen>>。例如..。

代码语言:javascript
复制
fun pens(): List<Ref<Pen>> = filter(Pen::class).map { 
    it as Ref<Pen> ) 
}

根据Kotlin documentation的说法,上面使用的as操作符是一个“不安全”的强制转换操作符。这是因为it.item可以是任何itemitem的子类型。如果it.item不是Pen,就会抛出异常。但是,因为您只过滤Pen项,所以它永远不会抛出异常。

票数 0
EN

Stack Overflow用户

发布于 2017-08-17 08:22:31

我通过将泛型参数移动到方法并执行不安全强制转换来使其工作,该转换在过滤器操作之后应该总是成功的。下面是完整的代码。

代码语言:javascript
复制
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<out T : Item> (val id: Int, val item: T)

class Container {
    private val seq = AtomicInteger(0)
    private val items: MutableList<Ref<Item>> = mutableListOf()
    fun <T: Item> add(item: T) =  items.add(Ref(seq.incrementAndGet(), item))

    private fun <T: Item> filter(cls: KClass<T>) : List<Ref<T>> = 
                items.filter{cls.isInstance(it.item)}.map{it as Ref<T>}

    fun pens(): List<Ref<Pen>> = filter(Pen::class)

    fun erasers(): List<Ref<Eraser>> = filter(Eraser::class)
}

fun main(args: Array<String>) {
    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)}
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45710060

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档