Scala:类型与嵌套泛型不匹配

时间:2016-06-20 15:01:01

标签: scala generics types

我已经创建了下面的人为例子来展示我想要完成的事情。我正在寻找一个工厂,它会吐出一个类,该类通过它将处理的特定类型的请求进行参数化。我以为我理解了仿制药,但现在我怀疑自己。 :)

object SourceFactory {
  def apply[T <: Source[_ <: RequestContext]](x: String): Source[_ <: RequestContext] = { 
    x match {
      case "a" => new FooSource
    }   
  }
}

object RequestContextFactory {
  def apply[T >: RequestContext](x: String): T = { 
    x match {
      case "a" => FooRequestContext()
    }   
  }
}

abstract class RequestContext
case class FooRequestContext() extends RequestContext

abstract class Source[T <: RequestContext] {
  def get(ctx: T): Unit
}

class FooSource extends Source[FooRequestContext] {
  def get(ctx: FooRequestContext): Unit = {}
}

object Test extends App {
  val source = SourceFactory("a")
  val ctx = RequestContextFactory("a")

  source.get(ctx)
}

结果:

  

编译器异常错误:第32行:类型不匹配;    发现:Evaluator__da15fb805d29b29227bc28ccae6cd07d2c04cb40_1274353927.Test.ctx.type(底层类型为RequestContext)    必需:_ $ 2         source.get(CTX)

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

Scala无法在TSourceFactory.apply中推断RequestContextFactory.apply,因为它不会出现在参数中。您正在呼叫SourceFactory[Nothing]RequestContextFactory[RequestContext](仅次于def apply[T >: RequestContext]中可能的拼写错误)。

您可以明确地提供T

object SourceFactory {
  def apply[T <: RequestContext](x: String): Source[T] = { 
    (x match {
      case "a" => new FooSource
    }).asInstanceOf[Source[T]]
  }
}

object RequestContextFactory {
  def apply[T <: RequestContext](x: String): T = { 
    (x match {
      case "a" => FooRequestContext()
    }).asInstanceOf[T]   
  }
}

object Test extends App {
  val source = SourceFactory[FooRequestContext]("a")
  val ctx = RequestContextFactory[FooRequestContext]("a")

  source.get(ctx)
}

注意你最终需要演员表:这应该暗示这是一个糟糕的设计!问题是您的返回类型取决于参数的。 Scala确实支持这种称为路径依赖类型的形式,但它不适用于此处。您还可以尝试删除类型参数并仅使用存在性:

object SourceFactory {
  def apply(x: String): Source[_ <: RequestContext] = { 
    x match {
      case "a" => new FooSource
    }
  }
}

object RequestContextFactory {
  def apply(x: String): RequestContext = { // equivalent to _ <: RequestContext
    x match {
      case "a" => FooRequestContext()
    }
  }
}

object Test extends App {
  val source = SourceFactory("a")
  val ctx = RequestContextFactory("a")

  source.get(ctx)
}

SourceFactoryResourceContextFactory中没有演员表,但他们的返回类型之间也没有任何关系,所以source.get(ctx)将无法编译!

根据您的要求,实际可行的是将返回值配对为单一类型:

case class SourceAndRequestContext[T <: RequestContext](src: Source[T], ctx: T) {
  def get() = src.get(ctx)
}

object SourceAndRequestContextFactory {
  def apply(x: String): SourceAndRequestContext[_ <: RequestContext] = x match {
    case "a" => SourceAndRequestContext(new FooSource, FooRequestContext())
  }
}