简体   繁体   English

如何确定类型参数是否是特征的子类型?

[英]How to determine whether a type parameter is a subtype of a trait?

Let's say I have the following types 假设我有以下类型

class Foo
trait Bar

Is there a way to make a method which takes in a Type parameter, T, and determine if that T is a Bar? 有没有办法制作一个接受Type参数T的方法,并确定该T是否为Bar? For example, 例如,

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

Sadly, isBar[Foo with Bar] is false because erasure seems to erase mixins. 可悲的是, isBar[Foo with Bar]false因为擦除似乎抹去了mixins。

Also, manifest[Foo with Bar] <:< manifest[Bar] is false 此外, manifest[Foo with Bar] <:< manifest[Bar]为false

Is this possible at all? 这有可能吗?

I looked at this question: How to tell if a Scala reified type extends a certain parent class? 我看了这个问题: 如何判断Scala的具体类型是否扩展了某个父类?

but that answer doesn't work with mixed-in traits as they seem to be erased as evidenced above. 但是这个答案不适用于混合特征,因为它们似乎被清除,如上所述。

This can be achieved with TypeTags (at least 2.10M7): 这可以通过TypeTag (至少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 provide a 1:1 translation of Scala types because they represent the types the compiler knows. TypeTags提供Scala类型的1:1转换,因为它们代表编译器知道的类型。 Therefore they are much more powerful than plain old Manifests: 因此它们比普通的旧Manifest强大得多:

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

With the method tpe we get full access to Scalas new Reflection: 使用方法tpe我们可以完全访问Scalas的新反射:

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  

It is possible to do this pre-2.10, just not (as far as I know) with manifests: 有可能在2.10之前做到这一点,而不是(据我所知)有清单:

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

It's a bit of a hack, but it works as desired. 这有点像黑客,但它可以按照需要运行。

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

scala> isBar[Foo]
res1: Boolean = false

You can resolve it without reflection by using 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

Another typeclass usage(more generic): 另一个类型类用法(更通用):

  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

Now, in REPL: 现在,在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 now belongs to scala reflect package. TypeTag现在属于scala reflect包。 One needs to add extra dependency to use it. 需要添加额外的依赖项才能使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM