简体   繁体   English

Scala:确定用于泛型的方法结果类型

[英]Scala: determine method result type for use in generics

In a third-party library there is a series of request classes, all of which derive from some common base class, which is generic and takes response class as a parameter: 在第三方库中有一系列请求类,所有这些请求类都派生自一些公共基类,它是通用的,并将响应类作为参数:

abstract class AbstractRequest[ResponseType] {
  …
  def execute(): ResponseType
}

class UserList {…}
class UserListRequest extends AbstractRequest[UserList] {…}

class Avatar {…}
class AvatarRequest extends AbstractRequest[Avatar] {…}

…

I want to write some generic method that takes a request instance, executes it several times in some special ways and delegates processing of the responses to a function supplied in arguments: 我想写一些泛型方法,它接受一个请求实例,以一些特殊的方式执行它几次,并将响应的处理委托给参数中提供的函数:

def specialMultiExecute(request: Req)(responseHandler: Resp => Unit): Unit = …

— to be called like: - 被称为:

val myRequest: UserListRequest = …
specialMultiExecute(myRequest){ userList => … }

The problem is that I need to somehow specify Req and Resp types in the specialMultiExecute declaration. 问题是我需要在specialMultiExecute声明中以某种方式指定ReqResp类型。 I tried the obvious approach: 我尝试了一种明显的方法:

def specialMultiExecute[Req <: AbstractRequest[Resp], Resp](request: Req)(responseHandler: Resp => Unit): Unit = …

— but Scala compiler fails to deduct generic argument types (an explicit specification like specialMultiExecute [UserListRequest, UserList] (myRequest){ userList => … } is required). - 但Scala编译器无法specialMultiExecute泛型参数类型(需要一个显式规范,如specialMultiExecute [UserListRequest, UserList] (myRequest){ userList => … }

In C++ in such case I could write a template function with a single template parameter Req , while making Resp to be determined as result type of the method Req::execute : 在C ++中,在这种情况下,我可以使用单个模板参数Req编写模板函数,同时将Resp确定为方法Req::execute结果类型

template<typename Req>
void specialMultiExecute(
    Req request,
    std::function<void (decltype(std::declval<Req>().execute()))> responseHandler
) {…}
//i.e. we use `decltype(std::declval<Req>().execute())` instead of Resp

Is there way to write something similar is Scala? 有没有办法写类似的东西是Scala?

I mean something like (in Scala-like pseudocode): 我的意思是(像Scala一样的伪代码):

def specialMultiExecute[Req <: AbstractRequest](request: Req)(responseHandler: ResultTypeOf(Req#execute) => Unit): Unit = …

It is a limitation of the type inference mechanism. 这是类型推断机制的限制。 The simplest way to solve it is to use an implicit evidence that Req is a subtype of AbstractRequest[ResponseType] . 解决它的最简单方法是使用隐式证据 ,即ReqAbstractRequest[ResponseType]子类型

Here is an example. 这是一个例子。

import scala.language.implicitConversions
import scala.reflect.runtime.universe.TypeTag

abstract class AbstractRequest[ResponseType] {
  def execute(): ResponseType
}

final case class User(id: Int, name: String)
final case class House(id: Int, price: Int)

class UserListRequest extends AbstractRequest[List[User]] {
  override def execute(): List[User] = List(User(id = 3, name = "Sasha"))
  override def toString: String = "UserListRequest"
}

final class RequestWrapper[Req, Resp](val request: Req) extends AnyVal {
  type ResponseType = Resp
}

implicit def request2wrapper[Req, Resp](request: Req)(implicit ev: Req <:< AbstractRequest[Resp]): RequestWrapper[Req, Resp] =
  new RequestWrapper(request)

def specialMultiExecute[Req, Resp](wrapper: RequestWrapper[Req, Resp])
                                  (responseHandler: wrapper.ResponseType => Unit)
                                  (implicit ev: Req <:< AbstractRequest[Resp], TTReq: TypeTag[Req], TTResp: TypeTag[Resp]): Unit = {
  val request: Req = wrapper.request
  val executionResult: Resp = request.execute()
  responseHandler(executionResult)
  println(TTReq)
  println(TTResp)
  println(request)
}

specialMultiExecute(new UserListRequest())(println)
// List(User(3,Sasha))
// TypeTag[UserListRequest]
// TypeTag[List[User]]
// UserListRequest

Reference for <:< . 参考 <:< Reference for "Dependent types" . 参考 “依赖类型”

Edit 编辑

Te above code example was modified to allow identification of the concrete Request and Response types being used. 修改上面的代码示例以允许识别正在使用的具体RequestResponse类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM