与Scala中的Existential类型一起使用时,通过TypeTag通过typeTag跟踪类型运行时类型 [英] Trait runtime type of type parameter through TypeTag when used with Existential type in Scala

查看:139
本文介绍了与Scala中的Existential类型一起使用时,通过TypeTag通过typeTag跟踪类型运行时类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有trait,带有类型参数.要获取运行时类型,请使用TypeTag.但是,当将此trait(及其类)与Collection中的existential type一起使用时,例如ListMapTypeTag为丢失".

I have trait with type parameter. To get the runtime type I use TypeTag. However, when this trait (and its classes) are used with existential type in a Collection, e.g. List or Map, TypeTag is "lost".

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

Here is an example of standard way to use 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

到目前为止,您可以看到,在特征Animal中定义和实现的方法typeT可以正常工作.但是,当与List和存在性类型一起使用时,它无法正常工作:

As you can see, so far so good, the method typeT defined and implemented in the trait Animal works. However, when used with List and existential types, it failed working:

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,怎么办?

Explicit casting (like the following) for sure worked. But most of the time what is given is List[Anima[_]]. If another level of TypeCast is necessary, and how?

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)不能像普通方法一样使用?

I understand that aa(0) is an Animal[_] which is the reason. But still, aa(0) is not only an Animal[_], but a Dog. Why the typeT (or TypeTag) could not be used as if it were a normal method?

推荐答案

此处的问题是typeT()是一种方法,因此它将根据您对Animal的了解返回不同的值.如果编译器可以证明您具有Animal[Int],则可以轻松获得TypeTag[Int].但是,对于List[Animal[_]]中的存在类型,您将丢失Animal中包含的类型信息.因此,当您从列表中选择任意元素时,您所知道的就是调用typeT时它是Animal[_],仅此而已. typeT不了解每个实例的类型参数T.在这种情况下,它无法证明这一点.

The problem here is that typeT() is a method, so it will return a different value depending on your knowledge of the Animal. If the compiler can prove you have an Animal[Int], then it can easily get a TypeTag[Int]. But with an existential type in List[Animal[_]], you lose the type information contained in Animal. So when you select an arbitrary element from the list, all you know is that it's an Animal[_] when typeT is called, and nothing else. typeT does not know about the type parameter T for each instance. It has no way of proving it in this context.

类型转换当然有效,因为asInstanceof[Animal[Cat]]告诉编译器忘记它所知道的.如果您在输入错误时当然会抛出ClassCastException.

The type cast of course works, because asInstanceof[Animal[Cat]] tells the compiler to forget what it knows. This if course can throw a ClassCastException when you get it wrong.

使它工作的一种方法是,在实例化Animal[T]时要求隐式TypeTag[T],并存储该值,而不是在方法中解析它.不幸的是,这意味着您不能使用特征.

One way you can get it to work is by requiring the implicit TypeTag[T] on instantiation of an Animal[T], and storing that value, rather than resolving it within a method. Unfortunately, this means you cannot use a trait.

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

或者,您可以在隐式参数上使用一些语法糖来表达它:

Alternatively, you could express it using a little syntactic sugar on the implicit parameters:

abstract class Animal[T: TypeTag] {
    val tpe = implicitly[TypeTag[T]].tpe
}

这篇关于与Scala中的Existential类型一起使用时,通过TypeTag通过typeTag跟踪类型运行时类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆