2012-06-20 11 views
17

Digamos que tengo los siguientes tipos¿Cómo determinar si un parámetro de tipo es un subtipo de un rasgo?

class Foo 
trait Bar 

¿Hay una manera de hacer un método que toma en un parámetro de tipo, T, y determinar si T es un bar? Por ejemplo,

def isBar[T <: Foo: Manifest] = 
    classOf[Bar].isAssignableFrom(manifest[T].erasure) 

Lamentablemente, isBar[Foo with Bar] es false porque parece borrado para borrar mixins.

Además, manifest[Foo with Bar] <:< manifest[Bar] es falso

Es esto posible en absoluto?

Miré a esta pregunta: How to tell if a Scala reified type extends a certain parent class?

pero esa respuesta no funciona con mixtos en los rasgos, ya que parecen ser borrado como se evidencia anteriormente.

Respuesta

20

Esto se puede lograr con TypeTags (al menos 2.10M7):

scala> class Foo; trait Bar 
defined class Foo 
defined trait Bar 

scala> import reflect.runtime.universe._ 
import reflect.runtime.universe._ 

scala> def isBar[A <: Foo : TypeTag] = typeOf[A].baseClasses.contains(typeOf[Bar].typeSymbol) 
isBar: [A <: Foo](implicit evidence$1: reflect.runtime.universe.TypeTag[A])Boolean 

scala> isBar[Foo] 
res43: Boolean = false 

scala> isBar[Foo with Bar] 
res44: Boolean = true 

TypeTags proporcionan una relación 1: 1 Traducción de tipos Scala porque representan los tipos del compilador sabe. Por lo tanto son mucho más potentes que los viejos manifiestos de civil:

scala> val fooBar = typeTag[Foo with Bar] 
fooBar: reflect.runtime.universe.TypeTag[Foo with Bar] = TypeTag[Foo with Bar] 

Con el método tpe tenemos acceso completo a Scalas nueva Reflexión:

scala> val tpe = fooBar.tpe // equivalent to typeOf[Foo with Bar] 
tpe: reflect.runtime.universe.Type = Foo with Bar 

scala> val tpe.<tab><tab> // lot of nice methods here 
=:=     asInstanceOf  asSeenFrom   baseClasses   baseType   contains   declaration   
declarations  erasure    exists    find    foreach    isInstanceOf  kind     
map     member    members    narrow    normalize   substituteSymbols substituteTypes  
takesTypeArgs  termSymbol   toString   typeConstructor  typeSymbol   widen 
+2

Como nota al pie: 'typeTag [Foo with Bar]' es una abreviatura útil para 'implícitamente [TypeTag [Foo with Bar]] '(muy parecido a' Predef.manifest' en <2.10). –

+0

Hablando de accesos directos, 'typeOf [T]' es un equivalente de 'typeTag [T] .tpe'. –

6

Es posible hacer esto antes de la 2.10, no (hasta donde yo sé) con manifiestos:

def isBar[T <: Foo](implicit ev: T <:< Bar = null) = ev != null 

Es un poco un truco, pero funciona como se desee.

scala> isBar[Foo with Bar] 
res0: Boolean = true 

scala> isBar[Foo] 
res1: Boolean = false 
3

Puede resolverlo sin reflexión mediante el uso de las clases de tipos:

trait IsBar[T] { 
    def apply():Boolean 
} 

trait LowerLevelImplicits { 
    implicit def defaultIsBar[T] = new IsBar[T]{ 
    def apply() = false 
    } 
} 

object Implicits extends LowerLevelImplicits { 
    implicit def isBarTrue[T <: Bar] = new IsBar[T] { 
    def apply() = true 
    } 
} 

def isBar[T<:Foo](t: T)(implicit ib: IsBar[T]) = ib.apply() 

scala> import Implicits._ 

scala> isBar(new Foo) 
res6: Boolean = false 

scala> isBar(new Foo with Bar) 
res7: Boolean = true 
0

Otro uso clase de tipos (más genérico):

trait SubClassGauge[A, B] { 
    def A_isSubclassOf_B: Boolean 
    } 

    implicit class IsSubclassOps[A](a: A) { 
    def isSubclassOf[B](implicit ev: SubClassGauge[A, B]): Boolean = ev.A_isSubclassOf_B 
    } 

    trait LowerLevelImplicits { 
    implicit def defaultSubClassGauge[A, B] = new SubClassGauge[A, B] { 
     override def A_isSubclassOf_B: Boolean = false 
    } 
    } 

    object Implicits extends LowerLevelImplicits { 
    implicit def subClassGauge[A <: B, B]: SubClassGauge[A, B] = new SubClassGauge[A, B] { 
     override def A_isSubclassOf_B: Boolean = true 
    } 
    } 

    trait Prime 
    class NotSuper 
    class Super extends Prime 
    class Sub extends Super 
    class NotSub 

Ahora, en REPL:

@ import Implicits._ 
import Implicits._ 
@ (new Sub).isSubclassOf[NotSuper] 
res29: Boolean = false 
@ (new Sub).isSubclassOf[Super] 
res30: Boolean = true 
@ (new Sub).isSubclassOf[Prime] 
res31: Boolean = true 
@ (new Super).isSubclassOf[Prime] 
res32: Boolean = true 
@ (new Super).isSubclassOf[Sub] 
res33: Boolean = false 
@ (new NotSub).isSubclassOf[Super] 
res34: Boolean = false 

TypeTag ahora pertenecen s para reflejar el paquete scala. Uno necesita agregar dependencia adicional para usarlo.

Cuestiones relacionadas