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

Scala如何从子类实例获取仅含父类字段的对象 无需逐个字段映射

直接使用asInstanceOf[Details]无法达到需求,因为转型只是改变了静态类型引用,实例本身还是Person类型,仍然携带所有Person类的字段。以下是几种无需手动逐个映射字段的实现方案:

方案1:运行时反射实现(无第三方依赖)

基于Java/Scala反射动态提取父类构造参数,反射生成Details实例,不需要修改原有类定义:

import scala.reflect.runtime.universe._

def extractParent[T: TypeTag, S <: T: TypeTag](subInstance: S): T = {
  val parentType = typeOf[T]
  // 获取父类主构造器
  val parentConstructor = parentType.decl(termNames.CONSTRUCTOR).asMethod
  // 提取父类构造器的参数名列表
  val paramNames = parentConstructor.paramLists.head.map(_.name.toString)
  // 从子类实例中读取对应字段的值
  val paramValues = paramNames.map { name =>
    val field = subInstance.getClass.getDeclaredField(name)
    field.setAccessible(true)
    field.get(subInstance)
  }
  // 构造父类实例
  val parentClass = runtimeMirror(getClass.getClassLoader).runtimeClass(parentType)
  parentClass.getConstructors.head.newInstance(paramValues: _*).asInstanceOf[T]
}

// 调用示例
val person = Person("Alex", 33, "Europe")
val details = extractParent[Details, Person](person)

方案2:Shapeless派生(Scala 2适用,编译期安全)

如果允许引入第三方依赖,使用Shapeless的LabelledGeneric可以实现编译期安全的字段自动提取,字段不匹配时会直接在编译阶段报错,不会留到运行时:

  1. 首先添加sbt依赖:
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
  1. 实现通用提取逻辑:
import shapeless._
import shapeless.labelled.FieldType

trait Extracter[A, B] {
  def extract(a: A): B
}

object Extracter {
  implicit def genericExtracter[A, B, AR <: HList, BR <: HList](
    implicit
    aGen: LabelledGeneric.Aux[A, AR],
    bGen: LabelledGeneric.Aux[B, BR],
    intersection: ops.hlist.Intersection.Aux[AR, BR, BR]
  ): Extracter[A, B] = (a: A) => bGen.from(intersection(aGen.to(a)))
}

def extract[A, B](a: A)(implicit extracter: Extracter[A, B]): B = extracter.extract(a)

// 调用示例
val details = extract[Person, Details](person)

方案3:Scala 3原生实现(无依赖,编译期安全)

如果使用Scala 3版本,可以直接基于内置的Mirror派生能力实现,无需引入任何第三方依赖,同时保留编译期安全校验:

import scala.deriving.Mirror
import scala.compiletime.*

inline def extract[S, P](s: S): P = {
  val sMirror = summon[Mirror.ProductOf[S]]
  val pMirror = summon[Mirror.ProductOf[P]]
  val sFieldMap = s.asInstanceOf[Product].productElementNames
    .zip(s.asInstanceOf[Product].productIterator)
    .toMap
  val pParamNames = constValueTuple[pMirror.MirroredElemLabels].toList.map(_.toString)
  val pParamValues = pParamNames.map(sFieldMap(_))
  pMirror.fromProduct(Tuple.fromArray(pParamValues.toArray)).asInstanceOf[P]
}

// 调用示例
val details = extract[Person, Details](person)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 17:45:03