I have code that boils down to a Factory initializing an object and then using that object again to do additional operations:
trait Factory[T] {
def initialize(): T;
def finish(t: T): Unit;
}
As I understand this, the result of initialize
should always be suitable to be passed to finish
for any one Factory
instance, regardless of T
.
The factory itself is called at a point where it isn't known what T
is:
object Minimal {
object StringFactory extends Factory[String] {}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
While variant (2) works, variant (1) doesn't:
type mismatch; found : Minimal.obj.type (with underlying type Any) required: _$6
but I can't figure out how to fix this. Is it even possible?
What does the compiler get by calling the wrapper
method that it can't figure out itself? From my point of view, obj
's type should be _$6
, as the compiler seems to name that capture of _
. How can I make the compiler realize that without having to introduce a whole new method for it?
Based on Régis' answer, I found out that the compiler infers obj: Factory.T
. From there, it was a small step to combine this with dk14's suggestion to use type TT = T
. The result is this, buth generic and statically typechecked, without introducing a wrapper method. Kudos to both!
To literally answer the original question
From my point of view, obj's type should be _$6, as the compiler seems to name that capture of _. How can I make the compiler realize that without having to introduce a whole new method for it?
by giving _$6
the explicit name TT
. Of course, the methods then need to use that name as well.
trait Factory[T] {
type TT = T
def initialize(): TT;
def finish(t: TT): Unit;
}
object Minimal {
object StringFactory extends Factory[String] {
def initialize(): TT = ""
def finish(t: TT): Unit = {}
}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj: factory.TT = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}