Let's say that for some good reason I want a generic HashMap that contains all types of objects. I also want to push any unsightly instanceof
-like type checks into the data structure. To this end, a method like getAs[T <: Any](key: String): Option[T]
would be nice to have.
class State extends HashMap[String, Any] {
override d开发者_开发百科ef +[T >: Any](elem: (String, T)): State = super.+(elem)
override def -(key: String): State = super.-(key)
def getAs[T](key: String): Option[T] = {
super.get(key) match {
case s: Some[T] => s
case _ => None
}
}
}
object State extends HashMap[String, Any] {
override def empty: State = super.empty
}
I also have the following implicit conversion defined in the package object:
implicit def fromHashMap(m: HashMap[String, Any]): State = m.asInstanceOf[State]
When I compile the above code I get the following type erasure warning:
State.scala:10: warning: non variable type-argument T in type pattern Some[T] is
unchecked since it is eliminated by erasure
case s: Some[T] => s
^
This is unfortunate, since the entire purpose of that statement if to check the type!
In this case, do I have an option other than resorting to the experimental Manifest features? Moreover, is there a better basic approach to accomplish this?
Edit:
I got this working with using Manifests. I was helped along greatly by this article on Stackoverflow. However, I am still curious if there is a cleaner way anyone can recommend.
Double Edit:
Here is the current version of this. Manifests solved my immediate issues. However, a major change I made was to make the State class a wrapper for a Map. In doing so, I lost the advantages of inheritance (i.e. now I have to explicitly expose each map method I need a la keySet
below.
class State(
map: HashMap[String, (Manifest[_], Any)] =
scala.collection.immutable.HashMap.empty[String, (Manifest[_], Any)]
) extends java.io.Serializable {
def +[T <: Any](elem: (String, T))(implicit m: Manifest[T]): State =
State(map.+((elem._1, (m, elem._2))))
def -(key: String): State = State(map.-(key))
def keySet = map.keySet
def getAs[T](key: String)(implicit m : Manifest[T]): Option[T] = {
map.get(key) match {
case Some((om: Manifest[_], o: Any)) =>
if (om <:< m) Some(o.asInstanceOf[T]) else None
case _ => None
}
}
}
object State {
def apply() = new State()
def apply(map: HashMap[String, (Manifest[_], Any)]) = new State(map)
def empty = State()
}
Thanks to everyone who has looked at this so far.
Update for Scala 2.10:
See the current implementation of State
using ClassTag
and friends here on GitHub. I plan to update it to use TypeTag
s once TypeCreator
s are serializable.
Note that while
case s: Some[T]
Doesn't work because Some
's type parameter is erased, this is different:
case s @ Some(_: T)
In this case, it doesn't work because T
is a type parameter itself. While this does not make a difference in your case (no, there's no way other than manifests), consider the following:
case s: Some[Int] // erased
case s @ Some(_: Int) // not erased
精彩评论