如何从动态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
相关产品推荐
相关产品推荐

