You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala代码重构:消除重复逻辑并解决隐式转换编译错误

问题:Scala重构模式匹配后JSON序列化失败(找不到隐式Writes)

我有一段通过模式匹配处理OCPP请求并返回响应的Scala代码,原代码中OCPPCallError和OCPPCallResult的构建逻辑存在大量重复,后续还要新增10个模式匹配分支,因此尝试重构,但编译失败,提示找不到Writes[Product with Serializable]的隐式转换。

原代码:

def handleProvisioningServiceCSRequests(provisioningCsRequest: ProvisioningCSRequest, chargingStationId: String, msgTypeId: Int, msgId: String): Future[OCPPCallResponse] = provisioningCsRequest match {
    case bootNotificationRequest: BootNotificationRequest => provisioningService.bootNotification(bootNotificationRequest, chargingStationId).runToFuture.materialize.map {
      case Failure(fail) => populateOCPPCallError(msgId, msgTypeId, OCPPCallErrorCode.InternalError(), fail.getMessage)
      case Success(response) => OCPPCallResult(
        messageTypeId = msgTypeId,
        messageId = msgId,
        payload = Json.toJson(response)
      )
    }
    case notifyReportRequest: NotifyReportRequest => provisioningService.notifyReport(notifyReportRequest, chargingStationId).runToFuture.materialize.map {
      case Failure(fail) => populateOCPPCallError(msgId, msgTypeId, OCPPCallErrorCode.InternalError(), fail.getMessage)
      case Success(response) => OCPPCallResult(
        messageTypeId = msgTypeId,
        messageId = msgId,
        payload = Json.toJson(response)
      )
    }
  }

重构后的代码(编译报错):

def handleProvisioningServiceCSRequests(provisioningCsRequest: ProvisioningCSRequest, chargingStationId: String, msgTypeId: Int, msgId: String): Future[OCPPCallResponse] = {
    val result = provisioningCsRequest match {
      case bootNotificationRequest: BootNotificationRequest => provisioningService.bootNotification(bootNotificationRequest, chargingStationId).runToFuture
      case notifyReportRequest: NotifyReportRequest => provisioningService.notifyReport(notifyReportRequest, chargingStationId).runToFuture
    }
    result.materialize.map {
      case Failure(fail) => populateOCPPCallError(msgId, msgTypeId, OCPPCallErrorCode.InternalError(), fail.getMessage)
      case Success(response) => OCPPCallResult(
        messageTypeId = msgTypeId,
        messageId = msgId,
        payload = Json.toJson(response)) // 报错:找不到Writes[Product with Serializable]的隐式转换
    }
  }

原因分析

重构后,result的类型被Scala编译器推断为Future[Product with Serializable]——这是BootNotificationResponse和NotifyReportResponse的公共超类型。而Json.toJson需要对应具体类型的隐式Writes实例,这个模糊的超类型并没有对应的Writes定义,因此编译失败。


解决方案

方案一:使用高阶函数封装重复逻辑(推荐,无需修改现有响应类型结构)

把错误处理和结果封装逻辑抽成一个高阶函数,利用上下文绑定确保每个具体响应类型都有对应的Writes:

// 封装重复的响应处理逻辑
private def wrapResponse[T: Writes](msgId: String, msgTypeId: Int): Future[T] => Future[OCPPCallResponse] = { future =>
  future.materialize.map {
    case Failure(fail) => populateOCPPCallError(msgId, msgTypeId, OCPPCallErrorCode.InternalError(), fail.getMessage)
    case Success(response) => OCPPCallResult(
      messageTypeId = msgTypeId,
      messageId = msgId,
      payload = Json.toJson(response)
    )
  }
}

// 重构后的主方法
def handleProvisioningServiceCSRequests(provisioningCsRequest: ProvisioningCSRequest, chargingStationId: String, msgTypeId: Int, msgId: String): Future[OCPPCallResponse] = provisioningCsRequest match {
  case req: BootNotificationRequest => wrapResponse(msgId, msgTypeId)(provisioningService.bootNotification(req, chargingStationId).runToFuture)
  case req: NotifyReportRequest => wrapResponse(msgId, msgTypeId)(provisioningService.notifyReport(req, chargingStationId).runToFuture)
  // 后续新增分支只需一行代码调用wrapResponse即可,完全避免重复
}

方案二:定义公共响应父类型并提供统一Writes

如果所有响应类型可以继承自同一个 trait,可以为这个父类型定义隐式Writes:

  1. 定义公共父类型:
trait OCPPServiceResponse
case class BootNotificationResponse(/* 字段 */) extends OCPPServiceResponse
case class NotifyReportResponse(/* 字段 */) extends OCPPServiceResponse
// 后续新增的响应类型都继承OCPPServiceResponse
  1. 为父类型定义隐式Writes:
import play.api.libs.json._

implicit val ocppServiceResponseWrites: Writes[OCPPServiceResponse] = Writes {
  case resp: BootNotificationResponse => Json.toJson(resp)
  case resp: NotifyReportResponse => Json.toJson(resp)
  // 后续新增响应类型时,在这里添加对应的case分支
}
  1. 指定result的类型为Future[OCPPServiceResponse]:
def handleProvisioningServiceCSRequests(provisioningCsRequest: ProvisioningCSRequest, chargingStationId: String, msgTypeId: Int, msgId: String): Future[OCPPCallResponse] = {
    val result: Future[OCPPServiceResponse] = provisioningCsRequest match {
      case req: BootNotificationRequest => provisioningService.bootNotification(req, chargingStationId).runToFuture
      case req: NotifyReportRequest => provisioningService.notifyReport(req, chargingStationId).runToFuture
    }
    result.materialize.map {
      case Failure(fail) => populateOCPPCallError(msgId, msgTypeId, OCPPCallErrorCode.InternalError(), fail.getMessage)
      case Success(response) => OCPPCallResult(
        messageTypeId = msgTypeId,
        messageId = msgId,
        payload = Json.toJson(response)
      )
    }
  }

内容的提问来源于stack exchange,提问作者joesan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:22:35