Type parameter type runtime type through TypeTag when used with an existential type in Scala

I have trait with type parameter. To get the type of runtime, I use TypeTag . However, when this trait (and its classes) is used with the existential type in the collection, for example. List or Map , TypeTag "lost."

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

As you can see how good this is, the typeT method defined and implemented in the Animal trait works. However, when used with List and existential types, it did not work:

 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 

Explicit casting (like the following) probably worked. But in most cases, List[Anima[_]] . If you need another level of TypeCast 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 

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

+1
source share
1 answer

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

A type that, of course, works because asInstanceof[Animal[Cat]] tells the compiler to forget what it knows. This is if a course can ClassCastException when you make a ClassCastException .

One way to make it work is to require an implicit TypeTag[T] when creating an instance of Animal[T] and save that value, rather than allowing it as part of the method. Unfortunately, this means that you cannot use the tag.

 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 can express this using a little syntactic sugar in implicit parameters:

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

Source: https://habr.com/ru/post/1216641/


All Articles