[英]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
声明中以某种方式指定Req
和Resp
类型。 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]
. 解决它的最简单方法是使用隐式证据 ,即Req
是AbstractRequest[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" . 参考 “依赖类型” 。
Te above code example was modified to allow identification of the concrete Request
and Response
types being used. 修改上面的代码示例以允许识别正在使用的具体Request
和Response
类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.