Scala 3.3 Akka HTTP/Actor类型不匹配错误求助
解决Scala 3.3 + Akka Actor/HTTP中的类型不匹配问题
问题背景
使用Scala 3.3结合Akka Actor构建Akka HTTP应用,在StarRegistry对象中创建恒星、行星、卫星时,希望将它们分别存储在Map[String, Set[HeavenlyBody]]的对应键下("stars"对应Set[CreateStar],"planets"对应Set[CreatePlanet],"moons"对应Set[CreateMoon]),通过starRoutes提供API实现创建和查询功能,但编译时出现类型不匹配错误:
Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala: replyTo ! Stars(map.get("stars").toSeq) Found: Seq[Set[com.example.HeavenlyBody]] Required: Seq[com.example.CreateStar] Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:199:45 replyTo ! Planets(map.get("planets").toSeq) Found: Seq[Set[com.example.HeavenlyBody]] Required: Seq[com.example.CreatePlanet] Type Mismatch Error: D:\Studio\WORK\akka\AKKA-HTTP\scala-akka-http-interstellar-IoT\src\main\scala\com\example\StarRegistry.scala:213:41 replyTo ! Moons(map.get("moons").toSeq) Found: Seq[com.example.HeavenlyBody] Required: Seq[com.example.CreateMoon]
错误原因
- 类型转换问题:
map.get("stars")返回Option[Set[HeavenlyBody]],调用toSeq后得到Seq[Set[HeavenlyBody]],但Stars构造器需要的是Seq[CreateStar],类型不兼容;同时存储的HeavenlyBody需要转型为具体子类。 - 不可变Map更新错误:原代码中
map("stars") += star试图直接修改不可变Map,这在Scala中不允许,必须生成新的Map实例。
修复后的代码
修改StarRegistry.scala中的核心逻辑,解决类型转换和Map更新问题:
sealed trait HeavenlyBody final case class CreateStar(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody final case class CreatePlanet(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody final case class CreateMoon(id: String, name: String, location: String, size: Option[Size]) extends HeavenlyBody final case class Stars(stars: immutable.Seq[CreateStar]) final case class Planets(planets: immutable.Seq[CreatePlanet]) final case class Moons(moons: immutable.Seq[CreateMoon]) final case class Size(radius: Long) object StarRegistry { // actor protocol sealed trait Command final case class GetStars(replyTo: ActorRef[Stars]) extends Command final case class NewStar(star: CreateStar, replyTo: ActorRef[ActionPerformed]) extends Command final case class GetPlanets(replyTo: ActorRef[Planets]) extends Command final case class NewPlanet(planet: CreatePlanet, replyTo: ActorRef[ActionPerformed]) extends Command final case class GetMoons(replyTo: ActorRef[Moons]) extends Command final case class NewMoon(moon: CreateMoon, replyTo: ActorRef[ActionPerformed]) extends Command final case class ActionPerformed(description: String) // 初始化时明确每个Set的类型 def apply(): Behavior[Command] = registry( Map( "stars" -> Set.empty[CreateStar].asInstanceOf[Set[HeavenlyBody]], "planets" -> Set.empty[CreatePlanet].asInstanceOf[Set[HeavenlyBody]], "moons" -> Set.empty[CreateMoon].asInstanceOf[Set[HeavenlyBody]] ) ) private def registry(map: Map[String, Set[HeavenlyBody]]): Behavior[Command] = Behaviors.receiveMessage { case GetStars(replyTo) => // 取出Set并安全转型为CreateStar,再转为Seq val stars = map.getOrElse("stars", Set.empty).collect { case s: CreateStar => s }.toSeq replyTo ! Stars(stars) Behaviors.same case NewStar(star, replyTo) => replyTo ! ActionPerformed(s"Star '${star.name}' created!") // 更新不可变Map,生成新实例 val updatedStars = map("stars") + star registry(map.updated("stars", updatedStars)) case GetPlanets(replyTo) => val planets = map.getOrElse("planets", Set.empty).collect { case p: CreatePlanet => p }.toSeq replyTo ! Planets(planets) Behaviors.same case NewPlanet(planet, replyTo) => replyTo ! ActionPerformed(s"Planet '${planet.name}' created!") val updatedPlanets = map("planets") + planet registry(map.updated("planets", updatedPlanets)) case GetMoons(replyTo) => val moons = map.getOrElse("moons", Set.empty).collect { case m: CreateMoon => m }.toSeq replyTo ! Moons(moons) Behaviors.same case NewMoon(moon, replyTo) => replyTo ! ActionPerformed(s"Moon '${moon.name}' created!") val updatedMoons = map("moons") + moon registry(map.updated("moons", updatedMoons)) } }
关键修改点
- 初始化Map:明确每个初始Set的类型,通过
asInstanceOf转为Set[HeavenlyBody],确保类型兼容。 - 查询时类型转换:使用
collect安全地将HeavenlyBody转型为具体子类,避免强制转换的风险,再转为Seq。 - 更新不可变Map:使用
map.updated生成新的Map实例,替换原有的键值对,符合Scala不可变集合的使用规范。
内容的提问来源于stack exchange,提问作者Vakindu
相关产品推荐
相关产品推荐

