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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 16:42:47