Reputation: 43
I am trying to create an object that store the instances that object created and avoid to re-load. I tried to create a mutable map on the object to store it, but I get a compilation error.
This is my code:
class Bar
class FooBar extends Bar
object Foo {
val loaded = scala.collection.mutable.Map[String, Bar]()
def apply[T <: Bar](implicit m: Manifest[T]): T = {
val className = m.toString()
if (loaded.contains(className)) {
return loaded(className)
}
val instance = m.runtimeClass.newInstance().asInstanceOf[T]
loaded(className) = instance
instance
}
}
But when I try to compile I get the error:
error: type mismatch;
found : Bar
required: T
return loaded(className)
There is another way to create the map dynamically and pass the T? Or solve this differently?
Upvotes: 0
Views: 329
Reputation: 127711
Your problem is in that you are returning loaded(className)
which is of type Bar
, not T
as is required by your function type signature. Unfortunately, you cannot encode such dependence between types of map keys and values, at least not with the standard map collection. You have to perform explicit cast. Here is a working example:
import scala.reflect.ClassTag
class Bar
class FooBar extends Bar
object Test {
val classMap = scala.collection.mutable.Map[Class[_], Bar]()
def getInstance[T <: Bar : ClassTag]: T = {
val ct = implicitly[ClassTag[T]]
classMap get ct.runtimeClass match {
case Some(bar) => bar.asInstanceOf[T]
case None =>
val instance = ct.runtimeClass.newInstance().asInstanceOf[T]
classMap(ct.runtimeClass) = instance
instance
}
}
}
object Main {
def main(args: Array[String]) {
println(Test.getInstance[FooBar])
println(Test.getInstance[Bar])
println(Test.getInstance[FooBar])
}
}
Note that I'm using ClassTag
instead of Manifest
because Manifest
is deprecated. Its modern equivalent is TypeTag
, but for this task ClassTag
is more than enough.
Update
Here are variants of the code as suggested by @0__. First:
import scala.reflect.ClassTag
class Bar
class FooBar extends Bar
object Test {
val classMap = scala.collection.mutable.Map[Class[_], Bar]()
def getInstance[T <: Bar : ClassTag]: T = {
val ct = implicitly[ClassTag[T]]
classMap get ct.runtimeClass match {
case Some(bar: T) => bar
case None =>
val instance = ct.runtimeClass.newInstance().asInstanceOf[T]
classMap(ct.runtimeClass) = instance
instance
}
}
}
object Main {
def main(args: Array[String]) {
println(Test.getInstance[FooBar])
println(Test.getInstance[Bar])
println(Test.getInstance[FooBar])
}
}
And second, the best one IMO:
import scala.reflect.ClassTag
class Bar
class FooBar extends Bar
object Test {
val classMap = scala.collection.mutable.Map[Class[_], Bar]()
def getInstance[T <: Bar : ClassTag]: T = {
val ct = implicitly[ClassTag[T]]
classMap.getOrElseUpdate(ct.runtimeClass, ct.runtimeClass.newInstance().asInstanceOf[Bar]).asInstanceOf[T]
}
}
object Main {
def main(args: Array[String]) {
println(Test.getInstance[FooBar])
println(Test.getInstance[Bar])
println(Test.getInstance[FooBar])
}
}
Upvotes: 1