0

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?

1 Answer 1

1

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])
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

You can use case Some(bar: T) => bar which I think matches properly due to the classtag. You can also (perhaps better) use getOrElseUpdate
@0__, yes, you're right. Every day I learn something new about Scala standard library)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.