Generics 无反射或强制转换的泛型类型筛选器

Generics 无反射或强制转换的泛型类型筛选器,generics,kotlin,reification,Generics,Kotlin,Reification,在科特林,有一种有限的形式。是否有任何方法可以使用物化来过滤泛型类型,而不使用getClass()或as或任何一种奇怪的注释,即只使用Is关键字?例如,我有以下结构: import java.util.* internal class Layout<out T : LayoutProtocol>(val t: T) { fun getName(): String { return t.getName() } } interface LayoutPr

在科特林,有一种有限的形式。是否有任何方法可以使用物化来过滤泛型类型,而不使用
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
如果我们查看
过滤器立场(…)
的源代码,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
}
/**
*返回一个列表,其中包含作为指定类型参数R实例的所有元素。
*/
public inline fun Iterable.filterIsInstance():列表{
返回filterIsInstanceTo(ArrayList())
}
/**
*将作为指定类型参数R实例的所有元素追加到给定的[destination]。
*/
公共内联有趣易用。筛选器实例(目的地:C):C{
如果(元素为R)目的地,则为(此中的元素)。添加(元素)
返回目的地
}
如果这在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
}
inlinefunfilterticals(from:Iterable):Iterable{
val dest=ArrayList()
for(from中的元素)
if(元素为布局)
目的地添加(元素)
返回目的地
}

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

layouts.filter { it.t is Horizontal }.forEach { println(it.getName()) }
它会打印出你想要的东西。即

Horizontal layouts:
Horizontal

由于类型擦除的原因,没有简单的方法可以做到这一点,但是如果您真的需要,并且性能/可读性/错误倾向性不是您担心的问题,您可以做一些技巧:

首先,让我们向
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) {}
    }
  }
}
现在它可以打印出你想要的

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
   */
}
fun main(args:Array){
val layouts=LinkedList()
layouts.add(Layout.create(Horizontal()))
layouts.add(Layout.create(Vertical()))
println(“水平布局:”)
layouts.genericFilterIsInstance().forEach{println(it.getName())}
/*印刷品:
水平布局:
水平的
*/
}

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

要补充这个答案,原因是
filterIsInstance
不起作用,因为列表只包含
Layout
实例。您需要创建子类来通过反射区分它们。谢谢!但是,如果要筛选的对象是参数化类型,该怎么办?我怎样才能得到这样的东西
layouts.filter{it is Layout}.forEach{println(it.getName())}
此外,
layouts.filter{it.t是水平的}
返回
List
,而不是
List
,因此为了将其转换为正确键入的版本,我们需要使用未选中的强制转换,即
layouts.filter{it.t是水平的}.map{it as Layout}
。我真正需要的是一个具有以下类型签名的函数:
fun filterLayouts(from:Iterable):Iterable
@brean,然后声明该函数:
fun Iterable.filterLayouts()=filter{it.t是t}Iterable
谢谢,这正是我想要的!但是你如何防止有人调用布局构造函数?或者有没有办法避免
Layout.create(…)
?@breandan Layout.create是创建
Layout
子类的方便方法。它是保存类型信息所必需的。
open class TypeLiteral<T> {
  val type: Type = getSuperclassTypeParameter(javaClass)

  companion object {
    fun getSuperclassTypeParameter(subclass: Class<*>) =
      (subclass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
  }
}
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
   */
}