筛选泛型类型而不反映或投射

在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 } 

由于类型擦除,没有简单的方法来做到这一点,但如果你真的想要的和性能/可读性/错误倾向性不是你所担心的,你可以做一些技巧:

首先,我们添加一个工厂方法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 */ } 

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

只需通过布局类的t: T属性筛选layouts LinkedList。

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

它会打印你想要的。 IE

 Horizontal layouts: Horizontal