Scala类型:如何限制子类的泛型类型?

时间:2015-09-29 08:28:06

标签: scala generics types bounds

我有一个特点:

trait OAuthService {
  def sendWithAuthorizationQueryParams[A](request: OAuthRequest)(implicit unmarshaller: Unmarshaller[ResponseEntity, A]): Future[A] = {
    val httpRequest = request.toHttpRequestWithAuthorizationQueryParams

    sendAndReceive(httpRequest, request.signature)
  }

  def sendWithAuthorizationHeader[A](request: OAuthRequest)(implicit unmarshaller: Unmarshaller[ResponseEntity, A]): Future[A] = {
    val httpRequest = request.toHttpRequestWithAuthorizationHeader

    sendAndReceive(httpRequest, request.signature)
  }

  protected def sendAndReceive[A](httpRequest: HttpRequest, id: String)(implicit unmarshaller: Unmarshaller[ResponseEntity, A]): Future[A]
}

我正在创建一个子类:

class StreamingOAuthService()(implicit val actorPlumbing: ActorPlumbing) extends OAuthService {
  private val log = LoggerFactory.getLogger(getClass())

  override protected def sendAndReceive[A](httpRequest: HttpRequest, id: String)(implicit unmarshaller: Unmarshaller[ResponseEntity, A]) = {
    log.debug(s"Http request: {}.", httpRequest)

    import actorPlumbing._

    val host = httpRequest.uri.authority.host.address()

    val connectionFlow: Flow[HttpRequest, HttpResponse, Future[OutgoingConnection]] = Http().outgoingConnectionTls(host)

    Source.single(httpRequest)
      .via(connectionFlow)
      .runWith(Sink.head)
  }
}

StreamingOAuthService中,我想将通用类型冻结为ResponseEntity。换句话说,我想指定StreamingOAuthService方法支持的唯一类型是ResponseEntity。如图所示,StreamingOAuthService.sendAndReceive无法编译,因为返回类型为Future[ResponseEntity]而非Future[A],由特征指定。

3 个答案:

答案 0 :(得分:2)

我更多地考虑了我之前的答案,即使在多种类型的表单中,它仍然不是很令人满意,因为您需要定义所有类型AB等等您要使用的任何基类实例,然后该子类只能接受每个方法的那些类型。

类型运算符(通用类型约束)看起来像是提供了更好的选项:

trait Base {

  type T

  def fun1[A](input: String)(implicit tp: A <:< T): A

  def fun2[A](input: Int)(implicit tp: A <:< T): A
}

class RestrictedSub extends Base {

  override type T = Double

  def fun1[A](input: String)(implicit tp: A <:< T): A = {
    ...
  }

  def fun2[A](input: Int)(implicit tp: A <:< T): A = {
    ...
  }
}

对于方法的任何调用,当且仅当<:<[A,T]是子类型时,编译器才能提供合适的隐式A <:< T(通常写为A,类似于二元运算符) T,因此在编译时不应该禁止任何不适当的调用。

对于不受限制的子类(特征的伴随对象中工厂方法的良好候选者),类型T可以根据需要设置为AnyAnyRef。 / p>

我注意到,我没有尝试使用它来构建具有隐式UnmarshallerFuture返回类型的特征的完全充实版本,这可能使正确的复杂化溶液

答案 1 :(得分:1)

您可以使用[A]参数化整个特征并去除特征中每个方法的参数化:

trait OAuthService [A]{
  def sendWithAuthorizationQueryParams(request: OAuthRequest)(implicit unmarshaller: Unmarshaller[ResponseEntity, A]): Future[A] = ...
  ...
}

然后限制StreamingOAuthService使用ResponseEntity:

class StreamingOAuthService()(implicit val actorPlumbing: ActorPlumbing) extends OAuthService[ResponseEntity] {
...
}

答案 2 :(得分:0)

我认为你的意思是你希望类型规范A被锁定到子类中的ResponseEntity。如果是这样,您可以尝试将一个抽象类型成员添加到特征和子类:

trait OAuthService {

  type A

  def sendWithAuthorizationQueryParams(request: OAuthRequest)(implicit unmarshaller: Unmarshaller[ResponseEntity, A]): Future[A] = {
    ...
  }

  ...
}

class StreamingOAuthService()(implicit val actorPlumbing: ActorPlumbing) extends OAuthService {
  private val log = LoggerFactory.getLogger(getClass())

  type A = ResponseEntity

  override protected def sendAndReceive(httpRequest: HttpRequest, id: String)(implicit unmarshaller: Unmarshaller[ResponseEntity, ResponseEntity]) = {
    ...
  }

  ...
}

请注意,这假设A对于实例中的所有方法都必须是相同的类型,即使在基本特征中也是如此。

如果这不是意图,可以扩展上述想法以定义每种方法的类型(尽管显然这可能会很快变得不合适)。

这是一个(简化)示例,可以提供您的想法:

trait Base {

  type A
  type B
  ...

  def fun1(input: String): A

  def fun2(input: Int): B

  ...
}

class Sub extends Base {

  type A = Double
  type B = Double
  ...

  def fun1(input: String): A = {
    input.toDouble
  }

  def fun2(input: Int): B = {
    input.toDouble
  }

  ...
}