以通用方式管理特征实例使用者

时间:2018-12-20 11:42:47

标签: scala generics types

假设我具有以下基本特征和案例类

sealed trait BaseTrait
case class Foo(x: Integer = 1) extends BaseTrait
case class Bar(x: String = "abc") extends BaseTrait

我想为可以处理BaseTrait实例的类创建一个通用接口,如下所示:

class FooProcessor(val model: FooModel) extends BaseProcessor[Foo] {
    val v: Option[Foo] = model.baseVar
}
class BarProcessor(val model: BarModel) extends BaseProcessor[Bar] {
    val v: Option[Bar] = model.baseVar
}

为此,我具有以下特征

trait BaseModel[T <: BaseTrait] {
    var baseVar: Option[T] = None
}
trait BaseProcessor[T <: BaseTrait] {
    def model: BaseModel[T]
    def process(x: T): Unit = model.baseVar = Option(x)
}

模型定义如下

class FooModel extends BaseModel[Foo]
class BarModel extends BaseModel[Bar]

现在让我们想象一下我的应用程序中有以下处理器

val fooProcessor = new FooProcessor(new FooModel)
val barProcessor = new BarProcessor(new BarModel)

我想以某种通用的方式来处理它们,

def func[T <: BaseTrait](p: T) {
    val c/*: BaseProcessor[_ >: Foo with Bar <: BaseTrait with Product with Serializable]*/ = p match {
        case _: Foo => fooProcessor
        case _: Bar => barProcessor
    c.process(p)
}

编译器对最后一行并不满意

  

类型不匹配;
   找到:T
   必填:_1

如果我正确理解,那基本上是编译器试图阻止barProcessor.process(Foo())发生。我尝试了几种解决方案来解决此问题并实现所需的行为:

  • 解决这个问题的最简单方法是在比赛案例中使用适当的BaseTrait实例调用适当的*Processor.process,这似乎无济于事。
  • 在BaseModel和BaseProcessor中使用抽象类型,一方面摆脱了BaseModel中某种不需要的类型参数,但编译器的抱怨仍然有效,我无法弄清楚是否有可能使之工作< / li>
  • 摆脱BaseModel的类型参数和约束,只需要在处理器中进行类型转换以获取正确的类型,但是显式类型转换也不是我真正想要的

像这样:

trait BaseModel {
    var baseVar: Option[BaseTrait] = None
}
trait BaseProcessor[T <: BaseTrait] {
    def model: BaseModel
    def process(x: T): Unit = model.baseVar = Some(x)
    def getBaseValue: T = model.baseVar.map(_.asInstanceOf[T])
}

我想有人还可以某种方式说服编译器,这两种类型(Processor的T和func参数p的T)是等效的,但这似乎有点过头了(而且我也不太确定如何可以完成)。

所以我的问题是:是否可以以某种简单的方式来完成我在这里想要实现的目标(以统一的方式管理处理器+每个处理器都知道其特定的BaseTrait类型)?有没有我所缺少的更好的模型?

更新

按照Tim's answer,使控制器隐式解决了问题,但是,如果您想拥有一个定义控制器的类,并且在其接口上具有“ func”,则编译器似乎不再能够正确解析隐式了。所以,如果我尝试做这样的事情

class ProcessorContainer {
    implicit val fooProcessor = new FooProcessor(new FooModel)
    implicit val barProcessor = new BarProcessor(new BarModel)
    def func[T <: BaseTrait](p: T) = typedFunc(p)
    private def typedFunc[T <: BaseTrait](p: T)(implicit processor: BaseProcessor[T]) =
        processor.process(p)
}

class Test {
    val processorContainer = new ProcessorContainer
    processorContainer.func(Foo())
    processorContainer.func(Bar())
}

我收到以下编译错误(一个用于Foo,一个用于Bar):

  

找不到参数处理器的隐式值:BaseProcessor [Foo]
  方法的参数不足

有没有解决的办法?我当然可以公开控制器,以便可以隐式传递它们,但是我不希望这样做。

1 个答案:

答案 0 :(得分:2)

您可以通过使处理器implicit并将其作为附加参数传递给func来创建简单的类型类:

implicit val fooProcessor = new FooProcessor(new FooModel)
implicit val barProcessor = new BarProcessor(new BarModel)

def func[T <: BaseTrait](p: T)(implicit processor: BaseProcessor[T]) =
  processor.process(p)

如果您将Foo传递给func,它将在其上调用FooProcessor.process,如果您将Bar传递给func,它将将调用{{ 1}}。