Suppose that I have the following trait that defines an interface and takes a couple of type parameters...
trait Foo[A, B] {
// implementation details not i
How about:
trait Foo[A, B]
trait Factory[A, B] {
def make(thing: Thing): Foo[A, B]
}
class Thing
object Foo {
def apply[A, B](thing: Thing)(implicit ev: Factory[A, B]) = ev.make(thing)
private case class FooImpl[A, B](thing: Thing) extends Foo[A, B]
private case class AnotherFooImpl[A, B](thing: Thing) extends Foo[A, B]
implicit val fooImplFactory: Factory[Int, String] = new Factory[Int, String] {
override def make(thing: Thing): Foo[Int, String] = new FooImpl[Int, String](thing)
}
implicit val anotherFooImplFactory: Factory[String, String] = new Factory[String, String] {
override def make(thing: Thing): Foo[String, String] = new AnotherFooImpl[String, String](thing)
}
And now:
def main(args: Array[String]): Unit = {
import Foo._
val fooImpl = Foo[Int, String](new Thing)
val anotherFooImpl = Foo[String, String](new Thing)
println(fooImpl)
println(anotherFooImpl)
}
Yields:
FooImpl(testing.X$Thing@4678c730)
AnotherFooImpl(testing.X$Thing@c038203)
Using TypeTags
(to overcome erasure of type parameters), we can call the respective hidden implementations based on the type parameters passed in to the apply method like below. It correctly instantiates the respective implementations but the type information for Foo
is lost, in fact its coming some garbage like _202
etc? I don't know why that is happening and how to retain the correct types for Foo. Maybe someone can throw light on this.
trait Foo[A,B]
object Foo {
def apply[A: TypeTag, B: TypeTag](thing: Thing) =
if(typeTag[A] == typeTag[Int])
FooImpl(thing)
else if(typeTag[A] == typeTag[String])
AnotherFooImpl(thing)
else
new Foo[Double,Double] {}
private case class FooImpl(thing: Thing) extends Foo[Int, String]
private case class AnotherFooImpl(thing: Thing) extends Foo[String, String]
}
Foo[Int,String](new Thing) // Foo[_202, _203] = FooImpl($sess.cmd123$Thing@50350b75)
The actual types for _203 and _203 are: ???
// type _203 >: String with _201, type _202 >: Int with _200
Foo[String,String](new Thing) //Foo[_202, _203] = AnotherFooImpl($sess.cmd123$Thing@51d80d6)