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

如何从动态SubObject列表生成Scala的Foobar实例?

从SubObject列表生成Foobar实例的实现方案

给定代码定义

类结构

case class Foobar(
  subObjectA: SubObjectA,
  subObjectB: SubObjectB,
  subObjectC: SubObjectC,
)

trait SubObject

case class SubObjectA(
  foo: Int
) extends SubObject

case class SubObjectB(
  bar: String
) extends SubObject

case class SubObjectC(
  baz: String
) extends SubObject

动态实例列表

val list = List(SubObjectA(1), SubObjectC("one"))

需求说明

该列表为动态生成,元素是SubObject的子类实例,无重复类型、顺序不定,子类共约20种,列表可能仅包含部分子类实例。需要从列表生成Foobar实例,将对应类型的实例赋值给Foobar的对应字段。

实现方案

方案一:foldLeft + copy 逐步构建

核心思路是用一个初始Foobar实例作为累加器,遍历列表时通过模式匹配识别类型,用copy方法更新对应字段:

// 初始化临时Foobar(用占位值,后续会被列表中的实例覆盖)
val initialFoobar = Foobar(
  subObjectA = SubObjectA(0),
  subObjectB = SubObjectB(""),
  subObjectC = SubObjectC("")
)

val foobar = list.foldLeft(initialFoobar) { (acc, obj) =>
  obj match {
    case a: SubObjectA => acc.copy(subObjectA = a)
    case b: SubObjectB => acc.copy(subObjectB = b)
    case c: SubObjectC => acc.copy(subObjectC = c)
    // 新增子类时添加对应的case分支即可
    case unknown => throw new IllegalArgumentException(s"未知SubObject类型: ${unknown.getClass.getName}")
  }
}

如果业务允许字段为可选值,可以修改Foobar定义为Option类型,避免占位值:

// 修改后的Foobar定义
case class Foobar(
  subObjectA: Option[SubObjectA] = None,
  subObjectB: Option[SubObjectB] = None,
  subObjectC: Option[SubObjectC] = None
)

val foobar = list.foldLeft(Foobar()) { (acc, obj) =>
  obj match {
    case a: SubObjectA => acc.copy(subObjectA = Some(a))
    case b: SubObjectB => acc.copy(subObjectB = Some(b))
    case c: SubObjectC => acc.copy(subObjectC = Some(c))
    case unknown => throw new IllegalArgumentException(s"未知SubObject类型: ${unknown.getClass.getName}")
  }
}

方案二:遍历列表收集字段值

直接遍历列表,用变量存储每个字段的实例,最后构建Foobar:

var subA: Option[SubObjectA] = None
var subB: Option[SubObjectB] = None
var subC: Option[SubObjectC] = None

list.foreach {
  case a: SubObjectA => subA = Some(a)
  case b: SubObjectB => subB = Some(b)
  case c: SubObjectC => subC = Some(c)
  case unknown => throw new IllegalArgumentException(s"未知SubObject类型: ${unknown.getClass.getName}")
}

// 字段必填时,检查是否存在实例,缺失则抛异常或提供默认值
val foobar = Foobar(
  subObjectA = subA.getOrElse(throw new IllegalStateException("缺少SubObjectA实例")),
  subObjectB = subB.getOrElse(throw new IllegalStateException("缺少SubObjectB实例")),
  subObjectC = subC.getOrElse(throw new IllegalStateException("缺少SubObjectC实例"))
)

扩展优化:类型类减少重复代码

由于子类多达20种,每次新增子类都修改模式匹配分支会比较繁琐。可以用类型类实现更优雅的扩展:

// 定义类型类,用于更新Foobar对应字段
trait FoobarUpdater[T <: SubObject] {
  def update(foobar: Foobar, obj: T): Foobar
}

// 为每个子类实现类型类实例
implicit val subAUpdater: FoobarUpdater[SubObjectA] = (foobar, obj) => foobar.copy(subObjectA = obj)
implicit val subBUpdater: FoobarUpdater[SubObjectB] = (foobar, obj) => foobar.copy(subObjectB = obj)
implicit val subCUpdater: FoobarUpdater[SubObjectC] = (foobar, obj) => foobar.copy(subObjectC = obj)

// 扩展Foobar的更新方法
implicit class FoobarOps(foobar: Foobar) {
  def update[T <: SubObject](obj: T)(implicit updater: FoobarUpdater[T]): Foobar = updater.update(foobar, obj)
}

// 构建Foobar
val initialFoobar = Foobar(SubObjectA(0), SubObjectB(""), SubObjectC(""))
val foobar = list.foldLeft(initialFoobar)(_.update(_))

新增子类时,只需添加对应的FoobarUpdater实例,无需修改核心构建逻辑,符合开闭原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 21:31:17