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])
}
}