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