Accessing Erased Type Parameter Information in Scala

One of the things holding scala back from being a more robust language is the fact that it runs on the JVM. (On the other hand, this is also one of its strengths–you can easily interoperate with existing Java code and libraries, as well as any other code that runs ont he JVM, like Groovy or Clojure.) Because Scala runs ont he JVM, it suffers from type erasure, which means that any generic type parameters that are in the code are lost after compilation, and are no longer present in the byte code, and thus at runtime. (This was a design decision made by the Java team when they introduced generics in Java 1.5, in order to preserve byte code backward-compatibility.)

In order to work around this, Scala introduced the Manifest class, which captures the type information during compilation, and allows you to access this type at runtime.  This was originally designed to allow for the creation of arrays of the generic type at runtime, but it can also be used for other custom generic types.  It uses implicit parameters to capture this information.

The problem (that I had), though is that you can’t seem to access a class’s type parameters directly in a method of that class.  So, this does’t work:

import scala.reflect.Manifest
class MyClass[T: Manifest] {
  def myType(implicit m: scala.reflect.Manifest[T]) = m.toString

You won’t get a compilation error, but you will just get ‘Nothing’ as your type inside your method:

scala> new MyClass[Long]
res17: MyClass[Long] = MyClass@2769aba2
scala> res15.myType
res18: java.lang.String = Nothing

This is because there was no type parameter to the method itself, and the [T] in the method’s implicit argument is not the same [T] that is used to parameterize the class.  If we add a type parameter to the ‘myType’ method, we can get closer to what we want:

class MyClass2[T: Manifest] {
  def myType[U](implicit m: scala.reflect.Manifest[U]) = m.toString

Then, we can try it out:

scala> new MyClass2[Long]
res19: MyClass2[Long] = MyClass2@6c6455ae
scala> res19.myType[Int]
res20: java.lang.String = Int

Okay, so now we are getting some type information out, but it really isn’t ideal.  Note that the type parameter inside the ‘myType’ method was ‘Int’, which was the parameter we passed to the method, not ‘Long’, which is what we passed to the class.  What if we want to get the class’s type parameter?  We certainly don’t want the calling code to have to keep track of the type parameters in multiple places (once at the constructor call, and once more at the method invocation).  So, we can add a second method to help smooth this out:

class MyClass3[T: Manifest] {
  def myType[U](implicit m: scala.reflect.Manifest[U]) = m.toString
  def myTypeWtihoutExtraParam = myType[T]

And, then, we get:

scala> new MyClass3[Long]
res30: MyClass3[Long] = MyClass3@543d8ee8
scala> res30.myTypeWtihoutExtraParam
res31: java.lang.String = Long

So, you can see that the type that was passed in to the class constructor as a generic parameter is now available in the method, without the method caller needing to supply it after the object is created.

A couple of things to note:

  • Don’t forget to import scala.reflect.Manifest
  • The type parameter in the constructor definition is context-scoped, so the ‘: Manifest’ part is important.  This technique won’t work if you just specify the parameter as ‘[T]’.  If you leave this off, you will get an error that reads, “No Manifest available for T.”
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

One Comment