我有一个能够知道某个对象是否是Manifest
类型实例的函数。我想将其迁移到TypeTag
版本。旧功能如下:
def myIsInstanceOf[T: Manifest](that: Any) =
implicitly[Manifest[T]].erasure.isInstance(that)
我一直在尝试使用TypeTag,现在我有了这个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 */
有没有更好的方法来完成这项任务?是否可以使用U
来省略参数化Any
(就像在旧函数中一样)?
答案 0 :(得分:20)
如果对删除的类型使用子类型检查就足够了,请按照Travis Brown在上述评论中的建议:
def myIsInstanceOf[T: ClassTag](that: Any) =
classTag[T].runtimeClass.isInstance(that)
否则,您需要明确拼出U
类型,以便scalac在类型标记中捕获其类型:
def myIsInstanceOf[T: TypeTag, U: TypeTag] =
typeOf[U] <:< typeOf[T]
答案 1 :(得分:8)
在您的特定情况下,如果您确实需要迁移现有代码并保持相同的行为,则需要ClassTag
。使用TypeTag
更精确,但正因为某些代码的行为会有所不同,所以(通常)你需要小心。
如果你确实想要TypeTag
,我们可以做得比上面的语法更好;呼叫站点的效果与省略U
相同。
根据Eugene的回答,必须拼写两种类型,而推导that
的类型是合乎需要的。给定一个类型参数列表,指定all或none; type currying可能会有所帮助,但是这种方法似乎更简单。让我们使用这个隐式类,也是2.10中的新类,在只有3行中定义我们的解决方案。
import scala.reflect.runtime.universe._
implicit class MyInstanceOf[U: TypeTag](that: U) {
def myIsInstanceOf[T: TypeTag] =
typeOf[U] <:< typeOf[T]
}
事实上我认为这样的名字更好(比如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
如果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
如果您想学习自己编写此类代码,可能会对以下所示变体的讨论感兴趣。
结构类型可以使上述定义更紧凑:
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}
然而,使用结构类型并不总是一个好主意,因为-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
问题是由于反射导致的减速,这是实施结构类型所必需的。修复它很容易,只需使代码更长一些,如上所示。
在上面的代码中,我写了[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}
如果仔细查看返回值的类型,可以看到它是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))
^
最后和最不重要的一个提示,您可能会感兴趣或不感兴趣:在此尝试中,您将两次传递TypeTag [T] - 因此您应该在: TypeTag
之后删除[T
。
def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) =
myInstanceToTpe(that) stat_<:< tag.tpe
答案 2 :(得分:0)
我使用上述建议来提出以下建议。欢迎提供反馈。
/*
Attempting to cast Any to a Type of T, using TypeTag
http://stackoverflow.com/questions/11628379/how-to-know-if-an-object-is-an-instance-of-a-typetags-type
*/
protected def toOptInstance[T: ClassTag](any: Any) =
classTag[T].runtimeClass.isInstance(any) match {
case true =>
Try(any.asInstanceOf[T]).toOption
case false =>
/*
Allow only primitive casting
*/
if (classTag[T].runtimeClass.isPrimitive)
any match {
case u: Unit =>
castIfCaonical[T](u, "void")
case z: Boolean =>
castIfCaonical[T](z, "boolean")
case b: Byte =>
castIfCaonical[T](b, "byte")
case c: Char =>
castIfCaonical[T](c, "char")
case s: Short =>
castIfCaonical[T](s, "short")
case i: Int =>
castIfCaonical[T](i, "int")
case j: Long =>
castIfCaonical[T](j, "long")
case f: Float =>
castIfCaonical[T](f, "float")
case d: Double =>
castIfCaonical[T](d, "double")
case _ =>
None
}
else None
}
protected def castIfCaonical[T: ClassTag](value: AnyVal, canonicalName: String): Option[T] ={
val trueName = classTag[T].runtimeClass.getCanonicalName
if ( trueName == canonicalName)
Try(value.asInstanceOf[T]).toOption
else None
}
答案 3 :(得分:0)
你也可以从TypeTag中捕获类型(进入类型别名),但前提是它没有被删除,所以它在函数内部不起作用:
How to capture T from TypeTag[T] or any other generic in scala?