我有一个通过套接字连接的客户端 - 服务器应用程序。我的MessageHandler类负责处理传入和传出的消息。我传递必要的参数来填充请求和回调,我希望在收到响应后调用它。我在具有唯一请求标识符的哈希映射中存储回调。收到响应后,我从哈希映射中获取回调,调用它并将响应作为参数传递。这是代码
class MessageHanlder {
val callbacks = new HashMap[String, (AnyRef) => Unit]
def sendAuthRequest(login: String, password: String, callback: Option[(AnyRef) => Unit]) {
val requestId = generateRequestId()
// create a packet with requestId, login and password
// send the packet
if(callback.isDefined) callbacks += ((requestId, callback.get))
}
private def generateRequestId() = // returns random string
def handleAuthResponse(authResponse: AuthResponse) {
val requestId = authResponse.requestId
val callbackOption = callbacks.get(requestId)
if(callbackOption.isDefined) callbackOption.get(authResponse)
}
def sendServerInfoRequest(callback: Option[(AnyRef) => Unit]) {
val requestId = generateRequestId()
// create a packet with requestId
// send the packet
if(callback.isDefined) callbacks += ((requestId, callback.get))
}
def handleServerInfoResponse(serverInfoResponse: ServerInfoResponse) {
val requestId = serverInfoResponse.requestId
val callbackOption = callbacks.get(requestId)
if(callbackOption.isDefined) callbackOption.get(serverInfoResponse)
}
我的问题是回调的参数类型。它可以是ServerInfoResponse或AuthResponse或任何其他响应类型。每个响应都有自己的一组字段,我想从回调中访问它们。要将回调保存到hashmap中,我必须将参数类型概括为AnyRef。所以在回调中我必须将AnyRef转换为具体类型,如此
val serverInfoCallback = (response: AnyRef) => {
val serverInfoResponse = response.asInstanceOf[ServerInfoResponse] // explicit cast
val name = serverInfoResponse.name
val numberOfCores = serverInfoResponse.numberOfCores
// so on
}
有没有办法避免施法?或者是否有更正确的方法来实现回调系统?
谢谢!
答案 0 :(得分:4)
如果响应类型不是静态已知的,您可以创建一个密封的特征响应,并使其他类型扩展它。
然后你可以使用模式匹配和一些关于检查所有情况的编译器保证。如果你不能使这些类型延伸到一个密封类型,你可以使用模式匹配,但编译器不会帮助你。
如果响应类型是静态已知的,您是否可以在问题中明确类型关系?
答案 1 :(得分:3)
我发现您的问题非常有趣,并尝试使用令人难以置信的shapeless库找到类型安全的解决方案。我们走了:
/* Responses get send to the callbacks */
abstract class Response
/* Callback ids identify callbacks and also specify the type of response
* a corresponding callback accepts.
*/
abstract class CallbackId[T <: Response]
/* Shapeless magic that ensures a type-safe mapping from identifiers to
* callbacks. Consider an implicit of type CME[CallbackId[R], R => Unit]
* as the evidence that "an id promising to identify a callback that
* accepts a response R actually maps to such a function."
*/
class CME[-K, V] /* CallbackMapEntry */
implicit val acceptAppleResponse =
new CME[CallbackId[AppleResponse], AppleResponse => Unit]
implicit val acceptPearResponse =
new CME[CallbackId[PearResponse], PearResponse => Unit]
implicit val acceptAnyResponse =
new CME[CallbackId[Response], Response => Unit]
/* Define some responses */
case class AppleResponse() extends Response
case class PearResponse() extends Response
case class PruneResponse() extends Response
/* Define some callbacks */
val appleResponseCallback1 = (r: AppleResponse) => {
println("[appleResponseCallback1]")
}
val appleResponseCallback2 = (r: AppleResponse) => {
println("[appleResponseCallback1]")
}
val pearResponseCallback = (r: PearResponse) => {
println("[pearResponseCallback]")
}
val anyResponseCallback = (r: Response) => {
println("[anyResponseCallback] r is a " + r.getClass.getSimpleName)
r match {
case appleR: AppleResponse => // ...
case pearR: PearResponse => // ...
case pruneR: PruneResponse => // ...
}
}
/* A couple of identifiers */
object appleCbId1 extends CallbackId[AppleResponse]
object appleCbId2 extends CallbackId[AppleResponse]
object pearCbId1 extends CallbackId[PearResponse]
object pearCbId2 extends CallbackId[PearResponse]
object someCbId extends CallbackId[Response]
/* Init list of callbacks */
val callbacks = HMap[CME](
appleCbId1 -> appleResponseCallback1,
appleCbId2 -> appleResponseCallback2,
pearCbId1 -> pearResponseCallback,
pearCbId2 -> pearResponseCallback,
someCbId -> anyResponseCallback
)
val appleCb = callbacks.get(appleCbId1).get
val someCb = callbacks.get(someCbId).get
appleCb(AppleResponse()) /* Fine */
someCb(AppleResponse()) /* Fine */
someCb(PearResponse()) /* Fine */
// appleCb(PruneResponse()) /* Rejected by the compiler */
abstract class Request[R <: Response] {
def id: CallbackId[R]
}
case class AppleRequest(id: CallbackId[AppleResponse])
extends Request[AppleResponse]
case class PearRequest(id: CallbackId[PearResponse])
extends Request[PearResponse]
case class RandomRequest(id: CallbackId[Response])
extends Request[Response]
def handleAppleRequest(r: AppleRequest) {
// Do something with the request
// Phone home
val cb = callbacks.get(r.id).get
cb(AppleResponse()) /* Fine */
// cb(PearResponse()) /* Rejected by the compiler */
}
handleAppleRequest(AppleRequest(appleCbId1))
由于解决方案是类型安全的(或至少尝试过),因此在较少“静态”的环境中初始化回调列表可能会更复杂,例如,如果回调是由(弱类型)创建的工厂或反思。