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可以实现编译期安全的字段自动提取,字段不匹配时会直接在编译阶段报错,不会留到运行时:
- 首先添加sbt依赖:
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
- 实现通用提取逻辑:
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
相关产品推荐
相关产品推荐

