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 important
}
I want to use the companion object as a factory for concrete implementations of the trait. I also want to force users to use the Foo
interface instead of sub-classing So I hide the concrete implementations in the companion object like so:
object Foo {
def apply[A, B](thing: Thing): Foo[A, B] = {
???
}
private case class FooImpl[A1, B1](thing: Thing) extends Foo[A1, B1]
private case class AnotherFooImpl[A2, B1](thing: Thing) extends Foo[A2, B1]
}
I want to be able to use the factory as follows:
val foo = Foo[A1, B1](thing) // should be an instance of FooImpl
val anotherFoo = Foo[A2, B1](thing) // should be an instance of AnotherFooImpl
How do I implement the apply
method to make this happen? This SO post seems close to the mark.
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)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.