Pregunta En Scala, ¿cómo puedo filtrar por tipos reificados en tiempo de ejecución?


Tengo una colección de Scala que contiene objetos de diferentes subtipos.

abstract class Base

class A extends Base

class B extends Base

val a1 = new A()
val a2 = new A()
val b = new B()
val s = List(a1, a2, b)

Me gustaría filtrar todo el A objetos o la B objetos. Puedo hacer esto fácilmente si sé el objeto que quiero filtrar en tiempo de compilación.

s.filter(_.isInstanceOf[A]) // Give me all the As
s.filter(_.isInstanceOf[B]) // Give me all the Bs

¿Puedo hacerlo si solo conozco el tipo de objeto para filtrar en tiempo de ejecución? Quiero escribir una función como esta.

def filterType(xs:List[Base], t) = xs.filter(_.isInstanceOf[t])

Dónde t indica si quiero objetos de tipo A o B.

Por supuesto que no puedo escribirlo de esta manera debido al borrado de tipos. ¿Existe una forma de Scala idiomática para solucionar esto utilizando etiquetas de tipo? He estado leyendo la documentación de la etiqueta de tipo Scala y las publicaciones relevantes de StackOverflow, pero no puedo entenderlo.


5
2018-05-03 00:22


origen


Respuestas:


Esto ha surgido algunas veces. Duplicar, alguien?

scala> trait Base
defined trait Base

scala> case class A(i: Int) extends Base 
defined class A

scala> case class B(i: Int) extends Base 
defined class B

scala> val vs = List(A(1), B(2), A(3))
vs: List[Product with Serializable with Base] = List(A(1), B(2), A(3))

scala> def f[T: reflect.ClassTag](vs: List[Base]) = vs collect { case x: T => x }
f: [T](vs: List[Base])(implicit evidence$1: scala.reflect.ClassTag[T])List[T]

scala> f[A](vs)
res0: List[A] = List(A(1), A(3))

6
2018-05-03 02:49



La borradura de tipo destruirá cualquier información en los parámetros de tipo, pero los objetos aún saben a qué clase pertenecen. Debido a esto, no podemos filtrar por tipos arbitrarios, pero podemos filtrar por clase o interfaz / rasgo. ClassTag es preferible TypeTag aquí.

import scala.reflect.ClassTag

def filterType[T: ClassTag](xs: List[Base]) = xs.collect {
  case x: T => x
}

Que podemos usar como:

scala> filterType[B](s)
res29: List[B] = List(B@42096939)

scala> filterType[Base](s)
res30: List[Base] = List(A@8dbc09c, A@625f8cc7, B@42096939)

Este método es seguro en tiempo de ejecución si tipo T no es genérico Si hubiera un class C[T] extends Base no pudimos filtrar de forma segura C[String].


1
2018-05-03 02:45