繁体   English   中英

在Scala中与Existential类型一起使用时,通过TypeTag特征运行时类型参数

[英]Trait runtime type of type parameter through TypeTag when used with Existential type in Scala

我有trait与类型参数。 要获取运行时类型,我使用TypeTag 但是,当此trait (及其类)与集合中的existential type (例如ListMap )一起使用时, 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在trait 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 )不能像普通方法那样使用?

这里的问题是typeT()是一个方法,因此它将根据您对Animal的了解返回不同的值。 如果编译器可以证明你有一个Animal[Int] ,那么它可以很容易地得到一个TypeTag[Int] 但是对于List[Animal[_]]的存在类型,您将丢失Animal包含的类型信息。 因此,当您从列表中选择一个任意元素时,您所知道的是,当typeT时它是一个Animal[_] ,而不是其他任何typeT typeT不知道每个实例的类型参数T 它无法在这种情况下证明它。

类型转换当然有效,因为asInstanceof[Animal[Cat]]告诉编译器忘记它知道什么。 当你弄错了,这个if课程会抛出ClassCastException

让它工作的一种方法是在TypeTag[T]的实例化中要求隐式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
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM