Scala - 如何为逆变类定义工厂方法?

标签 scala

我有一个数据源的通用参数特征,每个实际源都有案例类

trait AbstractSource {
  val name: String
}

case class ConcreteSource(name: String) extends AbstractSource

我还有一个作用于这个数据源的类的特征(源的逆变)

trait AbstractConsumer[-T <: AbstractSource] {
  def foo(inp: T): Unit
}

class ConcreteConsumer extends AbstractConsumer[ConcreteSource] {
  override def foo(inp: ConcreteSource): Unit =
    println(inp.name)
}

我想为我的管道方法创建一个工厂方法,它根据输入数据源创建正确的使用者。我已经尝试了以下但都有错误

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()

如果我理解正确,问题是我需要向管道提供 AbstractConsumer[T] 的子类型,但由于逆变类型参数,我不知道如何根据输入执行此操作。

最佳答案

恕我直言,使用类型类更容易解决这类问题,如下所示:

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()
}

然后具体源将实现类型类,如下所示:

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())
}

最后,您可以像这样使用它:

import ConcreteConsumer._ // Put the factory in scope.

val source = new ConcreteSource("john")

val consumer1 = AbstractConsumer[ConcreteSource]
val consumer2 = AbstractConsumer.fromSource(source)

如果工厂需要一些参数或其他东西,您可以调整代码。


可以看到运行的代码here .

关于Scala - 如何为逆变类定义工厂方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70981000/

相关文章:

scala - 创建 Spark Dataframe 的摘要

scala - 将模拟 Actor 注入(inject) Spray 路线进行测试

multithreading - 从另一个线程修改局部变量是什么意思?

java - 从 Spark 2.0 中的逗号分隔字符串行中获取不同的项目

scala - 在 Scala 中递归使用隐式方法

scala - 如何在 Scala 中覆盖 Java varargs 方法?

scala - 在Scala中,如何在不知道长度的情况下获取从第n个元素到列表末尾的列表切片?

scala - sbt:我可以将scala编译器插件的源代码放入需要使用该插件编译的项目中吗?

scala - 甚至无法尝试在 Scala 中加载类

scala - Akka 在响应非 Actor 代码时避免包装 future