2012-06-20 5 views
17

Powiedzmy mam następujące rodzajeJak ustalić, czy parametr typu jest podtypem cechy?

class Foo 
trait Bar 

Czy istnieje sposób, aby metodę, która trwa w parametrze Type, T, oraz ustalić, czy, że T jest bar? Na przykład,

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

Niestety isBar[Foo with Bar] jest false ponieważ usuwanie wydaje się wymazać wstawek.

Również manifest[Foo with Bar] <:< manifest[Bar] jest fałszywa

Czy to w ogóle możliwe?

Patrzyłem na to pytanie: How to tell if a Scala reified type extends a certain parent class?

ale odpowiedź nie działa z mieszanym w cech, ponieważ wydają się być usunięte jak wynika powyżej.

Odpowiedz

20

ten można osiągnąć z TypeTags (przynajmniej 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 zapewnić 1: 1 tłumaczenie typów Scala ponieważ reprezentują typy kompilator nie wie. Dlatego są one znacznie bardziej wydajny niż zwykły starych Manifest:

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

Dzięki metodzie tpe możemy uzyskać pełny dostęp do Scalas nowej refleksji:

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

Jako przypis: 'typeTag [Foo with Bar]' jest użytecznym skrótem dla 'niejawnie [TypeTag [Foo with Bar]]' (podobnie jak 'Predef.manifest' w <2.10). –

+0

Mówiąc o skrótach, 'typeOf [T]' jest odpowiednikiem 'typeTag [T] .tpe'. –

6

To jest możliwe, aby to zrobić pre-2.10, po prostu nie (o ile mi wiadomo) z manifestami:

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

To trochę hack, ale działa zgodnie z oczekiwaniami.

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

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

Można go rozwiązać bez refleksji przez korzystających typeclasses:

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

Kolejne zastosowanie typeclass (bardziej ogólne):

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 

Teraz w 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 należy teraz s do scala odzwierciedlają pakiet. Aby go użyć, trzeba dodać dodatkową zależność.

Powiązane problemy