给出代码
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]])
我在这里推理有什么问题?
答案 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
条件可能会阻止这种情况发生,但编译器不会使用这些条件来判断您是否返回了正确的类型。