I have several questions on Manifest
and TypeTag
. I understand that JVM does not know about Generics and erases the types. So I cannot do this
def factoryForAll[T] = new T // will not compile. Runtime doesn't know what T is
Scala compiler could transfer information about types to runtime using Manifest
(now deprecated). Manifest
has methods like erasure
which contains information about types. So I could do following to create objects of a generic type T
def factoryForall[T](implicit ev:Manifest[T]) = ev.erasure.newInstance
scala> factoryForAll[String]
res1:Any=""
scala> class C
defined class C
scala> factoryForAll[C]
res5: Any = C@52cb52bd
Question 1 - Interesting, it doesn't work for Int (or Float)? Why?
scala> factoryForAll[Int]
java.lang.InstantiationException: int
Question 2 - Why was Manifest deprecated? I understand that the newer version, TypeTag
has richer information but I do not understand what were the shortcomings in Manifest
Question 3 - Scala 2.12 still has Manifest
class (https://www.scala-lang.org/api/current/scala/reflect/Manifest.html). If Manifest
are bad, why do Scala still has it? The documentation refers to using Manifest
to create Arrays
of Generic types but the Arrays could be implemented by ClassTag
as well. So why does Scala still has Manifest
?
scala> def makeArray[T](len:Int)(implicit ev:ClassTag[T]) = new Array[T](len)
makeArray: [T](len: Int)(implicit ev: scala.reflect.ClassTag[T])Array[T]
scala> makeArray[String](4)
res39: Array[String] = Array(null, null, null, null)
scala> makeArray[Int](4)
res40: Array[Int] = Array(0, 0, 0, 0)
scala> val al = makeArray[List[Int]](2)
al: Array[List[Int]] = Array(null, null)
scala> al(0) = List(1)
scala> al(1) = List(2,3)
Coming to TypeTag
, there are 3 types. Referring to Scala documentation (http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html) and a tutorial on Medium (https://medium.com/@sinisalouc/overcoming-type-erasure-in-scala-8f2422070d20), I understand that TypeTag
and ClassTag
have different use cases. ClassTag cannot differentiate between types beyond 1st level of erasure.
//method to extract a type from a collection
def extractType[T](col:Iterable[Any])(implicit ev:ClassTag[T]) = {
val it =col.iterator
while (it.hasNext) {
val el = it.next
el match {
case x:T => println("got T")
case _ => println("not T")
}}}
extractType: [T](col: Iterable[Any])(implicit ev: scala.reflect.ClassTag[T])Unit
scala> extractType[Int](List(1,2,3,"hello"))
got T
got T
got T
not T
scala> extractType[List[Int]](List(List(1),List(2),List(3),List("hello")))
got T
got T
got T
got T //this should be not T
Question4: If ClassTag
cannot differentiate between 1st level of erasure, why do I get the following error when I try to add a String
in a List[Set[Int]]
. Isn't Int
erased?
scala> def makeArray[T](len:Int)(implicit ev:ClassTag[T]) = new Array[T](len)
makeArray: [T](len: Int)(implicit ev: scala.reflect.ClassTag[T])Array[T]
scala> val al = makeArray[List[Set[Int]]](2)
al: Array[List[Set[Int]]] = Array(null, null)
scala> al(0) = List(Set(2))
scala> al(1) = List(Set("2"))
<console>:28: error: type mismatch;
found : String("2")
required: Int
al(0) = List(Set("2"))
^
Question5 - why in the earlier example extractType[List[Int]](List(List(1),List(2),List(3),List("hello")))
, Scala couldn't distinguish String
from Int
but it distinguished between al(0) = List(Set(2))
and al(1) = List(Set("2"))
Question 6 - How do I change the extractType
function such that I can check embedded types. I know I have to use TypeTag but I do not know how to check the type of the element in the collection.
def extractType[T](col:Iterable[Any])(implicit ev:TypeTag[T]) = {
println("class is "+ev.mirror.runtimeClass) //I suppose in TypeTag, runtime is here
val it =col.iterator
while (it.hasNext) {
val el = it.next
el match {
case x:T => println("got T") //this doesn't compile. What should I check for?
case _ => println("not T")
}}}
Question 1: Int
and Float
aren't represented by classes in JVM (though they have corresponding Class
objects), let alone ones which have parameterless constructors. Despite the forAll
in the name, this works for a very limited set of types.
Question 2: Manifest
mixes up concerns which ClassTag
and TypeTag
separate.
Question 3: if you look at the source of Manifest
, it says
// TODO undeprecated until Scala reflection becomes non-experimental // @deprecated("use scala.reflect.ClassTag (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0")
Question 4/5: this error comes from the static type al: Array[List[Set[Int]]]
. No runtime information provided by ClassTag
or TypeTag
is involved.
Question 6: using only the standard library you can't. But see Shapeless.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With