Scala中的协方差和类型推断

时间:2013-05-20 04:40:10

标签: scala type-inference covariance

给出代码

object Renderer {

  sealed abstract class BasicRender

  case class RenderImages(img: Array[File]) extends BasicRender

  case class RenderVideo(video: File) extends BasicRender

  def rendererFor[T <: BasicRender : Manifest, Z <: Render.RenderingContext](ctx: Z): Option[Render[T]] = {
    val z = manifest[T].erasure
    if (z == classOf[RenderImages]) {
      Some(new ImagesRenderer(ctx.asInstanceOf[ImagesContext])) // .asInstanceOf[Render[T]])
    } else
    if (z == classOf[RenderVideo]) {
      Some(new VideoRenderer(ctx.asInstanceOf[VideoContext])) // .asInstanceOf[Render[T]])
    } else {
      None
    }
  }

  private class ImagesRenderer(ctx: ImagesContext) extends Render[RenderImages] {

    override def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = {
      None
    }

  }

  private class VideoRenderer(ctx: VideoContext) extends Render[RenderVideo] {

    override def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = {
      None
    }

  }


}

trait Render[+Out] {

  def renderJSON(json: String)(implicit jsCtx: PhantomJsContext): Option[Out]

}

我为它的类型参数设置了渲染特征协变,所以如果

RenderImages <: BasicRender 

然后

ImagesRenderer <: Render[RenderImages]

但看起来编译器无法在 rendererFor 中推断渲染器的类型,所以我需要添加显式类转换,如

Some(new ImagesRenderer(ctx.asInstanceOf[ImagesContext]).asInstanceOf[Render[T]])

我在这里推理有什么问题?

2 个答案:

答案 0 :(得分:6)

正如Daniel C. Sobral所解释的那样,你的问题在于你是动态地实例化不同的渲染器,这种方式不会在类型系统中捕获ctx与{{的结果类型之间的关系。 1}}。 解决此类问题的一种常见方法是使用类型

rendererFor

您可以轻松检查REPL是否返回了正确的类型:

import java.io.File

class PhantomJsContext

trait Renderer[+Out] {
  def renderJSON(json: String)(implicit jsCtx: PhantomJsContext): Option[Out]
}

trait RendererFactory[ContextType, ResultType] {
  def buildRenderer( ctx: ContextType ): Renderer[ResultType]
}

object Renderer {
  case class RenderImages(img: Array[File])
  case class RenderVideo(video: File)

  trait ImagesContext  
  trait VideoContext

  def rendererFor[ContextType, ResultType](ctx: ContextType)( implicit factory: RendererFactory[ContextType, ResultType] ): Renderer[ResultType] = {
    factory.buildRenderer( ctx )
  }

  class ImagesRenderer(ctx: ImagesContext) extends Renderer[RenderImages] {
    def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = ???
  }
  implicit object ImagesRendererFactory extends RendererFactory[ImagesContext, RenderImages] {
    def buildRenderer( ctx: ImagesContext ) = new ImagesRenderer( ctx )
  }

  class VideoRenderer(ctx: VideoContext) extends Renderer[RenderVideo] {
    def renderJSON(json: String)(implicit jsCtx: PhantomJsContext) = ???
  }
  implicit object VideoRendererFactory extends RendererFactory[VideoContext, RenderVideo] {
    def buildRenderer( ctx: VideoContext ) = new VideoRenderer( ctx )
  }
}

答案 1 :(得分:5)

不推断

T:它是传递给方法的参数,并且实际上并不能保证返回的内容是Option[Render[T]]。例如,假设您通过RenderImages并返回VideoRenderer,那么显然是错误的。

现在,您放入的if条件可能会阻止这种情况发生,但编译器不会使用这些条件来判断您是否返回了正确的类型。