1

我有一个通过套接字连接的客户端-服务器应用程序。我的 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
}

有什么办法可以避免铸造?还是有更正确的方法来实现回调系统?

谢谢!

4

2 回答 2

4

如果响应类型不是静态已知的,您可以制作一个密封的特征 Response 并让其他类型扩展它。

然后,您可以使用模式匹配和一些编译器保证来检查所有情况。如果您不能使这些类型扩展一种密封类型,则无论如何都可以使用模式匹配,但编译器不会帮助您。

如果响应类型是静态已知的,您能否在问题中明确类型关系?

于 2012-10-10T21:14:51.490 回答
3

我发现您的问题很有趣,并尝试使用令人难以置信的无形库找到类型安全的解决方案。开始了:

基本

/* 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))

由于该解决方案是类型安全的(或至少尝试是),因此在不太“静态”的环境中初始化回调列表可能会更复杂,例如,如果回调是由(弱类型)工厂或由反射。

于 2012-10-11T14:22:50.863 回答