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

时间:2018-11-21 19:57:02

标签: scala generics

在第三方库中,有一系列请求类,它们均来自某个通用基类,该通用基类是通用的,并且将响应类作为参数:

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

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

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

…

我想编写一些接受请求实例的通用方法,以某些特殊方式执行几次,并将响应的处理委托给参数中提供的函数:

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

—被称为:

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

问题是我需要以某种方式在Req声明中指定RespspecialMultiExecute类型。我尝试了明显的方法:

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

-但是Scala编译器无法推导通用参数类型(需要使用specialMultiExecute [UserListRequest, UserList] (myRequest){ userList => … }这样的显式规范)。

在这种情况下,我可以在C ++中用单个模板参数Req编写模板函数,同时使Resp被确定为方法Req::execute的结果类型< / em>:

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

是否可以编写类似Scala的东西?

我的意思是(用类似于Scala的伪代码):

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

1 个答案:

答案 0 :(得分:3)

这是类型推断机制的限制。 解决此问题的最简单方法是使用隐式证据,即ReqAbstractRequest[ResponseType]子类型

这里是一个例子。

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代表<:<。   Reference用于“从属类型”

编辑

修改了上面的代码示例,以允许识别所使用的具体RequestResponse类型。