0

コードを考えると

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]

}

Render trait を型パラメータの共変にしました。

RenderImages <: BasicRender 

それから

ImagesRenderer <: Render[RenderImages]

しかし、コンパイラは rendererFor でレンダラーのタイプを推測できないように見えるので、明示的なクラス キャストを追加する必要があります。

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

ここでの私の推論の何が問題になっていますか?

4

2 に答える 2

6

ctxDaniel C. Sobral が説明したように、ここでの問題は、 と の結果の型の関係を型システムでキャプチャしない方法で、さまざまなレンダラーを動的にインスタンス化していることですrendererFor。この種の問題を解決する一般的な方法の 1 つは、型クラスを使用することです。

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

正しい型が返されることを REPL で簡単に確認できます。

scala> lazy val r1 = Renderer.rendererFor( new Renderer.ImagesContext {} )
r1: Renderer[Renderer.RenderImages] = <lazy>

scala> :type r1
Renderer[Renderer.RenderImages]

scala> lazy val r2 = Renderer.rendererFor( new Renderer.VideoContext {} )
r2: Renderer[Renderer.RenderVideo] = <lazy>

scala> :type r2
Renderer[Renderer.RenderVideo]
于 2013-05-20T12:47:39.980 に答える