I have a trait with common parameters for a data source, with case classes for each actual source
trait AbstractSource {
val name: String
}
case class ConcreteSource(name: String) extends AbstractSource
I also have a trait for a class which acts on this data source (contravariant for the source)
trait AbstractConsumer[-T <: AbstractSource] {
def foo(inp: T): Unit
}
class ConcreteConsumer extends AbstractConsumer[ConcreteSource] {
override def foo(inp: ConcreteSource): Unit =
println(inp.name)
}
I want to create a factory method for my pipeline method which creates the correct consumer based on the input data source. I have tried the following but both have errors
object ConsumerFactory {
def create(inp: AbstractSource): AbstractConsumer[_ <: AbstractSource] =
inp match {
case _: ConcreteSource => new ConcreteConsumer()
case _ => ???
}
def createTwo[T <: AbstractSource](inp: T): AbstractConsumer[T] =
inp match {
case _: ConcreteSource => new ConcreteConsumer() // errors "required: AbstractConsumer[T], found: ConcreteConsumer"
case _ => ???
}
}
class Main {
def pipeline[T <: AbstractSource](consumer: AbstractConsumer[T], source: T): Unit =
consumer.foo(source)
def execute(): Unit = {
val consumer: ConcreteSource = ConcreteSource("john")
val source = ConsumerFactory.create(consumer) // errors "found: AbstractConsumer[_$1] where type _$1 <: AbstractSource, required: AbstractConsumer[ConcreteSource]"
val source = ConsumerFactory.createTwo(consumer)
pipeline(source, consumer)
}
}
val x = new Main()
x.execute()
If I understand correctly, the issue is that I need to supply a subtype of AbstractConsumer[T] to the pipeline, but I don't know how to do this based on the input due to the contravariant type parameter.