考虑抽象类 Fruit:
trait Fruit
case object Apple extends Fruit
case object Orange extends Fruit
我想为具有通用输入和输出的水果实现处理器服务:
trait FruitProcessorInput[T <: Fruit]
case class AppleProcessorInput(parm: List[Int]) extends FruitProcessorInput[Apple.type]
case class OrangeProcessorInput(parm: List[Int]) extends FruitProcessorInput[Orange.type]
trait FruitProcessorOutput[T <: Fruit]
case class AppleProcessorOutput(parm: List[Int]) extends FruitProcessorOutput[Apple.type]
case class OrangeProcessorOutput(parm: List[Int]) extends FruitProcessorOutput[Orange.type]
trait FruitProcessorService[T <: Fruit] {
def processFruit(input: FruitProcessorInput[T]): FruitProcessorOutput[T]
}
问题是我无法具体实现以下两个作品:
// THIS COMPILES:
case class AppleProcessorService() extends FruitProcessorService[Apple.type] {
override def processFruit(input: FruitProcessorInput[Apple.type]): FruitProcessorOutput[Apple.type] = ???
}
// THIS COMPILES:
case class AppleProcessorService() extends FruitProcessorService[Apple.type] {
override def processFruit(input: FruitProcessorInput[Apple.type]): AppleProcessorOutput = ???
}
但以下情况则不然:
// THIS DOES NOT COMPILE:
case class AppleProcessorService() extends FruitProcessorService[Apple.type] {
override def processFruit(input: AppleProcessorInput): AppleProcessorOutput = ???
}
主要是当我使用扩展类型
AppleProcessorInput
作为参数时,即使它扩展了FruitProcessorInput[T]
,scala也不将其视为类型FruitProcessorInput[Apple.type]
,而当它用于返回时,它接受FruitProcessorOutput[Apple.type]
的AppleProcessorOutput。为什么它在返回类型时有效,但在参数类型时无效
我不确定 scala 是否有充分的理由不允许这样做,或者是否有解决方法。
我有这个结构的原因是有一个我映射的通用 http4s api
val routes: HttpRoutes[IO] = Router[IO](
"/apples" -> service(appleService),
"/oranges" -> service(orangeService)
)
服务是什么样的
def service[T <: Fruit](
fruitService: FruitProcessorService[T]
)(implicit decoder: EntityDecoder[IO, FruitProcessorInput[T]]): HttpRoutes[IO] =
HttpRoutes.of[IO] {
case req @ GET -> Root =>
for {
input <- req.attemptAs[FruitProcessorInput[T]].fold(err => IO.raiseError(err), out => IO(out)).flatten
output <- fruitService.processFruit(input)
response <- Ok(output.asJson)
} yield response
}
问题是
FruitProcessorService[Apple]
必须接受任何FruitProcessorInput[Apple]
,否则会破坏Liskov。
但是,我们可以进一步调整界面以模拟您想要的内容。
请尝试进行以下更改,并让我知道它是否有效。
// Further refine FruitProcessorService:
trait FruitProcessorService[T <: Fruit] {
type Input <: FruitProcessorInput[T]
type Output <: FruitProcessorOutput[T]
def processFruit(input: Input): Output
}
// Adapt service to the new interface.
def service[T <: Fruit](
fruitService: FruitProcessorService[T]
)(implicit
decoder: EntityDecoder[IO, fruitService.Input]
encoder: EntityEncoder[IO, fruitService.Ouput]
): HttpRoutes[IO] =
HttpRoutes.of[IO] {
case req @ GET -> Root =>
req.as[fruitService.Input].flatMap { input =>
Ok(fruitService.processFruit(input))
}
}