Scala 3如何检测列表包含所有Shape子类实例(无需手动维护子类)
Scala 3 实现密封特质子类全包含检查方法
给定如下密封特质及子类定义:
sealed trait Shape final case class Circle(radius: Float) extends Shape final case class Square(side: Float) extends Shape final case class Rectangle(width: Float, height: Float) extends Shape
需要实现方法 def listContainsAtLeastOneOfEach(shapes: List[Shape]): Boolean,要求当列表包含所有Shape子类的至少一个实例时返回true,否则返回false,且无需手动维护子类列表。以下提供两种符合要求的实现方案:
方案一:基于内联与派生镜像(Mirror)
利用Scala 3的编译时内联和Mirror.SumOf自动获取密封特质的子类信息,无需手动维护子类列表:
import scala.deriving.Mirror import scala.compiletime.* sealed trait Shape final case class Circle(radius: Float) extends Shape final case class Square(side: Float) extends Shape final case class Rectangle(width: Float, height: Float) extends Shape object ShapeChecker { // 内联方法:检查列表是否包含指定类型的实例 private inline def containsInstance[T](shapes: List[Shape]): Boolean = shapes.exists(_.isInstanceOf[T]) // 内联遍历所有子类类型,逐一检查是否存在实例 private inline def checkAllSubtypes[M <: Mirror.SumOf[Shape]](shapes: List[Shape])(using mirror: M): Boolean = { // 获取所有子类对应的检查结果元组 inline val subtypeChecks = summonAll[Tuple.Map[M.MirroredElemTypes, [t] =>> Boolean]] // 遍历元组,验证所有检查结果为true inline def loop(checks: Tuple): Boolean = checks match { case EmptyTuple => true case head *: tail => head && loop(tail) } loop(subtypeChecks.map([t] => (_: Boolean) => containsInstance[t](shapes))) } def listContainsAtLeastOneOfEach(shapes: List[Shape]): Boolean = checkAllSubtypes(shapes) }
实现说明
Mirror.SumOf[Shape]会在编译时自动解析密封特质的所有子类,生成包含子类类型的元组M.MirroredElemTypes- 通过
summonAll为每个子类生成对应的检查逻辑,判断列表中是否存在该类型的实例 - 遍历所有检查结果,只有当所有子类都存在实例时,才返回
true
方案二:基于宏实现
通过Scala 3的宏在编译时获取密封特质的子类信息,生成对应的检查代码:
import scala.quoted.* sealed trait Shape final case class Circle(radius: Float) extends Shape final case class Square(side: Float) extends Shape final case class Rectangle(width: Float, height: Float) extends Shape object ShapeChecker { def listContainsAtLeastOneOfEach(shapes: List[Shape]): Boolean = ${ listContainsAtLeastOneOfEachImpl('shapes) } private def listContainsAtLeastOneOfEachImpl(shapesExpr: Expr[List[Shape]])(using Quotes): Expr[Boolean] = { import quotes.reflect.* // 获取密封特质Shape的所有子类类型 val shapeType = TypeRepr.of[Shape] val subtypes = shapeType.typeSymbol.children .filter(_.isClassDef) // 过滤出子类的类定义 .map(_.typeRef) // 为每个子类生成exists检查表达式 val checkExprs = subtypes.map { subtype => Expr.betaReduce('{ $shapesExpr.exists(_.isInstanceOf[${subtype.asType}] ) }) } // 将所有检查用逻辑与连接,空特质时默认返回true checkExprs.reduceOption((a, b) => '{ $a && $b }).getOrElse('{ true }) } }
实现说明
- 在宏中通过反射API获取
Shape的所有子类类型 - 为每个子类生成
shapes.exists(_.isInstanceOf[Subtype])的表达式 - 将所有表达式用
&&连接,最终生成整体的布尔判断代码
测试用例
object Test extends App { val allShapes = List(Circle(1.0f), Square(2.0f), Rectangle(3.0f,4.0f)) println(ShapeChecker.listContainsAtLeastOneOfEach(allShapes)) // 输出 true val missingCircle = List(Square(2.0f), Rectangle(3.0f,4.0f)) println(ShapeChecker.listContainsAtLeastOneOfEach(missingCircle)) // 输出 false val emptyList = List.empty[Shape] println(ShapeChecker.listContainsAtLeastOneOfEach(emptyList)) // 输出 false }
内容的提问来源于stack exchange,提问作者Angel Blanco
相关产品推荐
相关产品推荐

