2

Basically, I want to have a Map indexed by type objects. In this case, I'm trying to use Class as the "type type".

the following code:

class SuperClass {}
val typemap = new HashMap[Class[_ <: SuperClass], SuperClass]

def insertItem1[T <: SuperClass] (item : T) = typemap += classOf[T] -> item
def insertItem2[T <: SuperClass] (item : T) = typemap += item.getClass -> item

does not compile, because (1) classOf[T] is apparently invalid:

error: class type required but T found

and (2) item.getClass is of the wrong type:

Type mismatch, expected: Class[_ <: SuperClass], actual: Class[_]

I see two ways to accomplish this:
either drop the type requirement and use Class[_] only
or use Manifest instead of Class (not sure how to do that yet, any examples are welcome)

But more generally, why is classOf[T] invalid, and how do I obtain a class object for a parametrized type?

update: I found out that I can use item.getClass.asInstanceOf[Class[T]] to get to the class object. For one, this is ugly. For two, it doesn't help much, because later in the code i have functions such as this one:

def isPresent[T <: SuperClass] = typemap contains classOf[T]

Obviously, I could use the asInstanceOf method in insertItem and pass the class as a parameter to isPresent. Is there a better way?

matejcik
  • 1,912
  • 16
  • 26
  • 1
    Why do you need to store and retrieve objects by type? I wonder if given more context, there isn't a different solution that doesn't involve `classOf` or `getClass` at all... – huynhjl Oct 03 '11 at 01:18
  • I believe this is a duplicate of http://stackoverflow.com/questions/7335946/class-type-as-key-in-map-in-scala – Sohum Banerjea Oct 03 '11 at 01:21
  • @huynhjl the usecase is an Entity System where I want to be able to retrieve, basically, all (registered) objects for a given type. So I could define a special kind of key for each type, but the Class object -is- that key. what it could do without is generics, i could simply pass the Class objects as keys around and then it would work fine. But i thought that there is a better way. – matejcik Oct 03 '11 at 18:10

2 Answers2

2

You can use manifests to achieve this:

class TypeMap extends HashMap[Class[_], Any] {
    def putThing[T](t: T)(implicit m: Manifest[T]) = put(m.erasure, t)
}

val map = new TypeMap
map.putThing(4)
map.get(classOf[Int]) // returns Option[Any] = Some(4)

You are losing the types through erasure, so you need to add the implicit argument to the putThing which contains the name of the class, using a Manifest.

Matthew Farwell
  • 60,889
  • 18
  • 128
  • 171
  • What I don't understand is how can T be lost by erasure in putThing - after all, I can get it through `t.getClass`, so it must be known at run-time. – matejcik Oct 03 '11 at 18:34
1

The problem is once again that generics aren't available at run time. The compiler does the task of figuring out what T is, but it can't because it is only know at run time.

You could use a manifest to obtain that information: What is a Manifest in Scala and when do you need it?.

Community
  • 1
  • 1
Jens Schauder
  • 77,657
  • 34
  • 181
  • 348