
254
CHAPTER 8
■ PARSERS—BECAUSE BNF IS NOT JUST FOR ACADEMICS ANYMORE
class SafeMapC[A, B](m: Map[A, B]) {
def sGet[T](key: A)(implicit man: Manifest[T]): Option[T] =
m.get(key).flatMap(v => new SafeCast(v).is(man))
}
implicit def mToSM[A, B](in: Map[A, B]) = new SafeMapC(in)
implicit def iToIs(v: Any) = new SafeCast(v)
}
When the Java Virtual Machine was introduced in 1996, the Java language did not
include generics. In Java 1.0, an
ArrayList contained only Object. You had to manually cast
each element of
ArrayList into the class that you wanted. Java 1.5 introduced generics so
that at compile time you could specify
ArrayList<String>. This avoided the manual cast
because the compiler did the casting for you. However, the JVM was not updated to keep
track of the generics associated with
ArrayList<?>, so under the covers, an ArrayList<String>
is just an
ArrayList, not an ArrayList<String>. The type, <String>, is erased, which means
it is known at compile time but not at runtime. This design was perpetuated through to Scala.
Scala introduced a feature in 2.7.2 called
Manifests. They were an experimental, undoc-
umented feature, but they are very, very useful. They allow a method implicitly to take a
Manifest as a parameter for each type parameter passed to the method. The Manifest
contains information including the type that is to be erased when the bytecode is rendered.
Thus, the
Manifest allows us to reify the erased type.
8
We will use this feature in our SafeMap trait to do syntactically pleasing, type-safe casting.
First, we define a
SafeMap trait that we can mix into any class or object and make use of
type-safe access to
Maps.
trait SafeMap {
We import the scala.reflect package to the Manifest:
import scala.reflect._
We define the SafeCast class. It has a single method, is, which takes an implicit Manifest
as a parameter and tests to see whether the class of
in is assignable from the class contained
by the
Manifest and accessed using the erasure method.
class SafeCast(in: Any) {
def is[T](implicit man: Manifest[T]): Option[T] = {
// special case for Boolean
val cls = if (man.toString == "boolean") classOf[java.lang.Boolean]
else man.erasure
8. See http://scala-blogs.org/2008/10/manifests-reified-types.html.
19897ch08.fm Page 254 Wednesday, April 22, 2009 3:54 PM