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

在Scala中与存在类型一起使用时,通过TypeTag指定类型参数的特征运行时类型,scala,runtime,traits,existential-type,Scala,Runtime,Traits,Existential Type,我有带类型参数的trait。要获取运行时类型,我使用TypeTag。但是,当此特征(及其类)与集合中的存在类型一起使用时,例如列表或映射,类型标签将“丢失” 以下是使用类型标记的标准方法示例: scala> import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe._ scala> trait Animal[T] { | def typeT()(implicit t: T

我有带类型参数的
trait
。要获取运行时类型,我使用
TypeTag
。但是,当此
特征
(及其类)与集合中的
存在类型
一起使用时,例如
列表
映射
类型标签
将“丢失”

以下是使用类型标记的标准方法示例:

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
如您所见,到目前为止,在trait
Animal
中定义和实现的方法
typeT
效果良好。但是,当与
列表
和存在类型一起使用时,它无法工作:

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[\u]]
。如果需要另一个级别的类型转换,如何进行

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)
是一种
动物[\u]
,这就是原因。但是,
aa(0)
不仅是一种
动物[\u]
,还是一种
狗。为什么不能将
typeT
(或
TypeTag
)当作正常方法使用?

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

当然,类型转换是有效的,因为
asInstanceof[Animal[Cat]]
告诉编译器忘记它知道的内容。当您出错时,此if课程会抛出一个
ClassCastException

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

我懂了。在存在的情况下,类型参数在方法调用时“丢失”。我猜它也适用于两个类型参数(例如,Animal[S,T]),但正确的“隐式”语法是什么<代码>抽象类Animal[S,T](隐式ss:TypeTag[S],隐式tt:TypeTag[T]){…}
不起作用。我不会说它在方法调用时丢失了,但向上转换为
Animal[\uz]
。这个方法就是没有办法恢复它。您需要删除第二个
隐式
关键字。它只需要一次。@因果关系你会说这回答了你的问题吗?你回答了我的问题。我在这里发布了一个快速跟进问题:。你也能回答一下吗?