5

在 Kotlin 中有一种有限形式的具体化泛型。有没有办法使用具体化来过滤泛型类型而不使用getClass()as或任何奇怪的注释,即。仅仅通过使用is关键字?例如,我有以下结构:

import java.util.*

internal class Layout<out T : LayoutProtocol>(val t: T) {
    fun getName(): String {
        return t.getName()
    }
}

interface LayoutProtocol {
    fun getName(): String
}

internal class Vertical : LayoutProtocol {
    override fun getName(): String {
        return "Vertical"
    }
}

internal class Horizontal : LayoutProtocol {
    override fun getName(): String {
        return "Horizontal"
    }
}

fun main(args: Array<String>) {
    val layouts = LinkedList<Layout<*>>()
    layouts.add(Layout<Horizontal>(Horizontal()))
    layouts.add(Layout<Vertical>(Vertical()))
    println("Horizontal layouts:")
    layouts.filterIsInstance<Layout<Horizontal>>().forEach { println(it.getName()) }
}

这输出:

Horizontal layouts:
Horizontal
Vertical

我希望它输出以下内容。有没有办法获得:

Horizontal layouts:
Horizontal

如果我们查看 的源代码filterIsInstance(...),Kotlin 做了一些棘手的事情来规避类型擦除,但仍然不起作用:

/**
 * Returns a list containing all elements that are instances of specified type parameter R.
 */
public inline fun <reified R> Iterable<*>.filterIsInstance(): List<@kotlin.internal.NoInfer R> {
    return filterIsInstanceTo(ArrayList<R>())
}

/**
 * Appends all elements that are instances of specified type parameter R to the given [destination].
 */
public inline fun <reified R, C : MutableCollection<in R>> Iterable<*>.filterIsInstanceTo(destination: C): C {
    for (element in this) if (element is R) destination.add(element)
    return destination
}

如果这在 Kotlin 中是不可能的,是否有任何语言(JVM 或非 JVM)可以让我执行以下操作:

inline fun <reified R: LayoutProtocol> filterVerticals(from: Iterable<Layout<R>>): Iterable<Layout<Vertical>> {
    val dest = ArrayList<Layout<Vertical>>()

    for (element in from)
        if (element is Layout<Vertical>)
            dest.add(element)

    return dest
}
4

2 回答 2

2

由于类型擦除,没有简单的方法可以做到这一点,但如果你真的想要并且性能/可读性/易错性不是你担心的事情,你可以做一些技巧:

首先,让我们添加一个工厂方法Layout来保存擦除的类型

open internal class Layout<T : LayoutProtocol>(val t: T) {
  ...
  companion object {
    inline fun <reified T: LayoutProtocol> create(instance: T): Layout<T> {
      return object: Layout<T>(instance) {}
    }
  }
}

(注意:为了简单起见,我在这里删除了方差)

二、你需要一个辅助类

open class TypeLiteral<T> {
  val type: Type = getSuperclassTypeParameter(javaClass)

  companion object {
    fun getSuperclassTypeParameter(subclass: Class<*>) =
      (subclass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
  }
}

(注意: Guice DI使用相同的方法,它包含一个生产就绪的TypeLiteral实现)

最后,我们自己的过滤方法

inline fun <reified R> Iterable<*>.genericFilterIsInstance() where R : Any =
  filterIsInstance<R>()
  .filter { object : TypeLiteral<R>() {}.type == it.javaClass.genericSuperclass }

现在它可以打印出你想要的东西

fun main(args: Array<String>) {
  val layouts = LinkedList<Layout<*>>()
  layouts.add(Layout.create(Horizontal()))
  layouts.add(Layout.create(Vertical()))
  println("Horizontal layouts:")
  layouts.genericFilterIsInstance<Layout<Horizontal>>().forEach { println(it.getName()) }
  /* prints:
  Horizontal layouts:
  Horizontal
   */
}

但是请不要在生产代码中使用这个答案。在现实生活中,传递一个类实例进行过滤总是更可取的。

于 2017-03-06T09:57:49.787 回答
1

只需按Layout 类的属性过滤layoutsLinkedList 。t: T

layouts.filter { it.t is Horizontal }.forEach { println(it.getName()) }

它将准确打印您想要的内容。IE

Horizontal layouts:
Horizontal
于 2017-03-05T22:08:11.120 回答