[英]Scala - how to define factory method for contravariant class?
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)我还有一个 class 的特征,它作用于这个数据源(源逆变)
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.如果我理解正确,问题是我需要向管道提供 AbstractConsumer[T] 的子类型,但由于逆变类型参数,我不知道如何根据输入执行此操作。
IMHO, theese kinds of problems are more easily solvable with a typeclass, like this:恕我直言,这些类型的问题更容易通过类型类解决,如下所示:
trait AbstractConsumer[-S <: AbstractSource] {
def consume(inp: S): Unit
}
object AbstractConsumer {
sealed trait ConsumerFactory[S <: AbstractSource] {
type Consumer <: AbstractConsumer[S]
def createConsumer(): Consumer
}
type Factory[S <: AbstractSource, C <: AbstractConsumer[S]] = ConsumerFactory[S] { type Consumer = C }
object Factory {
def apply[S <: AbstractSource, C <: AbstractConsumer[S]](factory: => C): Factory[S, C] =
new ConsumerFactory[S] {
override final type Consumer = C
override final def createConsumer(): Consumer =
factory
}
}
// Get by type.
def apply[S <: AbstractSource](implicit factory: ConsumerFactory[S]): factory.Consumer =
factory.createConsumer()
// Get by value.
def fromSource[S <: AbstractSource](source: S)(implicit factory: ConsumerFactory[S]): factory.Consumer =
factory.createConsumer()
}
Then the concrete source will implement the typeclass, like this:然后具体源代码将实现类型类,如下所示:
final class ConcreteConsumer extends AbstractConsumer[ConcreteSource] {
override def consume(inp: ConcreteSource): Unit =
println(inp.name)
}
object ConcreteConsumer {
implicit final val ConcreteConsumerFactory: AbstractConsumer.Factory[ConcreteSource, ConcreteConsumer] =
AbstractConsumer.Factory(new ConcreteConsumer())
}
And, finally, you can use it like this:最后,您可以像这样使用它:
import ConcreteConsumer._ // Put the factory in scope.
val source = new ConcreteSource("john")
val consumer1 = AbstractConsumer[ConcreteSource]
val consumer2 = AbstractConsumer.fromSource(source)
You may adapt the code if the factory needs some arguments or something.如果工厂需要一些 arguments 或其他东西,您可以修改代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.