我有trait
类型参数。要获取运行时类型,我使用TypeTag
。但是,当此trait
(及其类)与集合中的existential type
一起使用时,例如List
或Map
,TypeTag
“丢失”。
以下是使用Type Tag的标准方法示例:
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> trait Animal[T] {
| def typeT()(implicit t: TypeTag[T]) = t.tpe
| }
defined trait Animal
scala>
scala> class Dog extends Animal[Int]
defined class Dog
scala> class Cat extends Animal[String]
defined class Cat
scala>
scala> val dog = new Dog
dog: Dog = Dog@4aa88c93
scala> val cat = new Cat
cat: Cat = Cat@2281e252
scala> dog.typeT
res46: reflect.runtime.universe.Type = Int
scala> cat.typeT
res47: reflect.runtime.universe.Type = String
正如您所看到的,到目前为止,特性typeT
中定义和实现的方法Animal
有效。但是,当与List
和存在类型一起使用时,它无法正常工作:
scala> val aa: List[Animal[_]] = List(dog, cat, dog)
aa: List[Animal[_]] = List(Dog@4aa88c93, Cat@2281e252, Dog@4aa88c93)
scala> aa(0).typeT
res52: reflect.runtime.universe.Type = _$1
scala> aa(1).typeT
res53: reflect.runtime.universe.Type = _$1
显式投射(如下所示)肯定有效。但大多数时候给出的是List[Anima[_]]
。如果需要另一级TypeCast,以及如何?
scala> aa(0)
res55: Animal[_] = Dog@4aa88c93
scala> aa(0).asInstanceOf[Dog]
res56: Dog = Dog@4aa88c93
scala> aa(0).asInstanceOf[Dog].typeT
res57: reflect.runtime.universe.Type = Int
我理解aa(0)
是Animal[_]
,这就是原因。但是,aa(0)
不仅是Animal[_]
,还有Dog
。为什么typeT
(或TypeTag
)无法像普通方法那样使用?
答案 0 :(得分:3)
此处的问题是typeT()
是一种方法,因此根据您对Animal
的了解,它会返回不同的值。如果编译器可以证明您有Animal[Int]
,则可以轻松获得TypeTag[Int]
。但是对于List[Animal[_]]
中的存在类型,您将丢失Animal
中包含的类型信息。因此,当您从列表中选择任意元素时,您只知道在调用Animal[_]
时它是typeT
,而不是其他任何元素。 typeT
不了解每个实例的类型参数T
。在这种情况下,它无法证明它。
类型转换当然有效,因为asInstanceof[Animal[Cat]]
告诉编译器忘记它知道的内容。如果你错了,这个if课程会抛出ClassCastException
。
可以让它工作的一种方法是在TypeTag[T]
的实例化上要求隐式Animal[T]
,并存储该值,而不是在方法中解析它。不幸的是,这意味着你不能使用特征。
abstract class Animal[T](implicit tt: TypeTag[T]) {
val tpe = tt.tpe
}
class Dog extends Animal[Int]
class Cat extends Animal[String]
val dog = new Dog
val cat = new Cat
scala> val aa: List[Animal[_]] = List(cat, dog, cat)
aa: List[Animal[_]] = List(Cat@5a9faacf, Dog@675c379d, Cat@5a9faacf)
scala> aa(0).tpe
res6: reflect.runtime.universe.Type = String
scala> aa(1).tpe
res7: reflect.runtime.universe.Type = Int
或者,您可以在隐式参数上使用一点语法糖来表达它:
abstract class Animal[T: TypeTag] {
val tpe = implicitly[TypeTag[T]].tpe
}