在您的特定情况下,如果您确实需要迁移现有代码并保持相同的行为,则需要ClassTag
. 使用TypeTag
更精确,但正是因为某些代码的行为会有所不同,所以(通常)你需要小心。
如果你确实想要TypeTag
,我们可以比上面的语法做得更好;在调用处的效果与省略 相同U
。
推荐的替代品
使用拉皮条
对于 Eugene 的回答,必须拼写两种类型,而最好推断that
. 给定一个类型参数列表,要么指定全部,要么不指定;类型柯里化可能会有所帮助,但只是拉皮条似乎更简单。让我们使用这个在 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
更兼容的语法
如果拉皮条是一个问题,因为它改变了调用语法并且你有现有的代码,我们可以尝试如下类型的柯里化(使用起来更棘手),这样只需要显式传递一个类型参数 - 就像在你的旧定义中一样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
after [T
。
def myIsInstanceOf[T: TypeTag, U: TypeTag](tag: TypeTag[T], that: U) =
myInstanceToTpe(that) stat_<:< tag.tpe