如何知道对象是否是TypeTag类型的实例?

jes*_*slg 19 scala instanceof scala-2.10

我有一个函数,它能够知道一个对象是否是一个Manifest类型的实例.我想将它迁移到一个TypeTag版本.旧功能如下:

def myIsInstanceOf[T: Manifest](that: Any) = 
  implicitly[Manifest[T]].erasure.isInstance(that)
Run Code Online (Sandbox Code Playgroud)

我一直在试验TypeTags,现在我有了这个TypeTag版本:

// Involved definitions
def myInstanceToTpe[T: TypeTag](x: T) = typeOf[T]
def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) = 
  myInstanceToTpe(that) stat_<:< tag.tpe

// Some invocation examples
class A
class B extends A
class C

myIsInstanceOf(typeTag[A], new A)        /* true */
myIsInstanceOf(typeTag[A], new B)        /* true */
myIsInstanceOf(typeTag[A], new C)        /* false */
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来完成这项任务?参数化是否U可以省略,使用Any替代(就像在旧函数中一样)?

Eug*_*ako 20

如果对擦除类型使用子类型检查就足够了,就像Travis Brown在上面的评论中所建议的那样:

def myIsInstanceOf[T: ClassTag](that: Any) =
  classTag[T].runtimeClass.isInstance(that)
Run Code Online (Sandbox Code Playgroud)

否则,您需要明确拼写出U类型,以便scalac在类型标记中捕获其类型:

def myIsInstanceOf[T: TypeTag, U: TypeTag] =
  typeOf[U] <:< typeOf[T]
Run Code Online (Sandbox Code Playgroud)


Bla*_*ade 8

在您的特定情况下,如果您确实需要迁移现有代码并保持相同的行为,则需要ClassTag.使用TypeTag更精确,但正是因为某些代码的行为会有所不同,所以(一般情况下)你需要小心.

如果你确实想要TypeTag,我们可以比上面的语法做得更好; 呼叫站点的效果与省略相同U.

推荐替代品

使用拉皮条

根据尤金的回答,人们必须拼写这两种类型,而推导它的类型是合乎需要的that.给定一个类型参数列表,指定all或none; 类型currying可能会有所帮助,但只是pimp方法似乎更简单.让我们使用这个隐式类,也是2.10中的新类,用3行定义我们的解决方案.

import scala.reflect.runtime.universe._
implicit class MyInstanceOf[U: TypeTag](that: U) {
  def myIsInstanceOf[T: TypeTag] = 
    typeOf[U] <:< typeOf[T]
}
Run Code Online (Sandbox Code Playgroud)

事实上,我认为像这样的名字(比如说更好stat_isInstanceOf)甚至可以归属于Predef.

使用示例:

//Support testing (copied from above)
class A
class B extends A
class C

//Examples
(new B).myIsInstanceOf[A]                //true
(new B).myIsInstanceOf[C]                //false

//Examples which could not work with erasure/isInstanceOf/classTag.
List(new B).myIsInstanceOf[List[A]]      //true
List(new B).myIsInstanceOf[List[C]]      //false

//Set is invariant:
Set(new B).myIsInstanceOf[Set[A]]        //false
Set(new B).myIsInstanceOf[Set[B]]        //true

//Function1[T, U] is contravariant in T:
((a: B) => 0).myIsInstanceOf[A => Int]   //false
((a: A) => 0).myIsInstanceOf[A => Int]   //true
((a: A) => 0).myIsInstanceOf[B => Int]   //true
Run Code Online (Sandbox Code Playgroud)

更兼容的语法

如果pimping是一个问题,因为它改变了调用语法并且你有现有的代码,我们可以尝试键入currying(使用起来比较棘手),因此只需要显式传递一个类型参数 - 就像在旧的定义中一样Any:

trait InstanceOfFun[T] {
  def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]): Boolean
}
def myIsInstanceOf[T] = new InstanceOfFun[T] {
  def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]) = 
    typeOf[U] <:< typeOf[T]
}
myIsInstanceOf[List[A]](List(new B))     //true
Run Code Online (Sandbox Code Playgroud)

如果您想学习自己编写此类代码,可能会对下面显示的变体的讨论感兴趣.

其他变化和尝试失败

结构类型可以使上述定义更紧凑:

scala> def myIsInstanceOf[T] = new { //[T: TypeTag] does not give the expected invocation syntax
  def apply[U: TypeTag](that: U)(implicit t: TypeTag[T]) = 
    typeOf[U] <:< typeOf[T]
}
myIsInstanceOf: [T]=> Object{def apply[U](that: U)(implicit evidence$1: reflect.runtime.universe.TypeTag[U],implicit t: reflect.runtime.universe.TypeTag[T]): Boolean}
Run Code Online (Sandbox Code Playgroud)

然而,使用结构类型并不总是一个好主意,因为-feature警告:

scala> myIsInstanceOf[List[A]](List(new B))
<console>:14: warning: reflective access of structural type member method apply should be enabled
by making the implicit value language.reflectiveCalls visible.
This can be achieved by adding the import clause 'import language.reflectiveCalls'
or by setting the compiler option -language:reflectiveCalls.
See the Scala docs for value scala.language.reflectiveCalls for a discussion
why the feature should be explicitly enabled.
              myIsInstanceOf[List[A]](List(new B))
                            ^
res3: Boolean = true
Run Code Online (Sandbox Code Playgroud)

问题是由于反射导致的减速,这是实施结构类型所必需的.修复它很容易,只需使代码更长一些,如​​上所示.

我不得不避免的陷阱

在上面的代码中,我写了[T]而不是[T: TypeTag]我的第一次尝试.有趣的是它失败了.要理解这一点,看看:

scala> def myIsInstanceOf[T: TypeTag] = new {
     |   def apply[U: TypeTag](that: U) = 
     |     typeOf[U] <:< typeOf[T]
     | }
myIsInstanceOf: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])Object{def apply[U](that: U)(implicit evidence$2: reflect.runtime.universe.TypeTag[U]): Boolean}
Run Code Online (Sandbox Code Playgroud)

如果仔细查看返回值的类型,可以看到它implicit TypeTag[T] => U => implicit TypeTag[U](以伪Scala表示法).当你传递一个参数时,Scala会认为它是第一个参数列表,隐含的一个:

scala> myIsInstanceOf[List[A]](List(new B))
<console>:19: error: type mismatch;
 found   : List[B]
 required: reflect.runtime.universe.TypeTag[List[A]]
              myIsInstanceOf[List[A]](List(new B))
                                          ^
Run Code Online (Sandbox Code Playgroud)

一个提示

最后也是最少,一个可能或不感兴趣的提示:在这次尝试中,你传递TypeTag [T]两次 - 因此你应该删除: TypeTag之后[T.

def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) = 
  myInstanceToTpe(that) stat_<:< tag.tpe
Run Code Online (Sandbox Code Playgroud)