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

Scala 2.13中如何实现Case Class的多类型联合?

在Scala 2.13中实现多类型约束的Case Class字段

问题背景

我定义了三个case class:

case class Teacher(name: String, subject: String, age: Int)
case class Student(name: String, subject: String, section: String, class: Int)
case class School(user: Teacher | Student)

希望School的user字段只能是Teacher或Student中的一种,请问在Scala 2.13中该如何实现?

补充1

有评论建议用Either,但如果需要支持三种类型呢?比如:

case class School(user: Teacher | Student | Principal)

补充2

我尝试用sealed trait实现,但写法存在问题,不清楚如何实例化。比如怎么创建包含Principal(name="ABC",year=2022)的School实例?我的代码如下:

sealed trait SchoolUser
case class Student(name: String, subject: String, section: String, `class`: Int)
case class Teacher(name: String, subject: String, age: Int)
case class Principal(name: String, year: Int)

case object Student extends SchoolUser
case object Teacher extends SchoolUser
case object Principal extends SchoolUser

case class School(user: SchoolUser)

val school = School(user = ???)

println("Complete")

解决方案

1. 修正Sealed Trait的写法

你之前的代码错误在于重复定义了同名的case class和case object,导致Student/Teacher/Principal的case class并未继承SchoolUser特质。正确的写法是让每个实体case class直接继承SchoolUser,不需要额外的case object:

// 密封特质,限定所有实现类必须在当前文件中定义
sealed trait SchoolUser

// 让每个实体类继承SchoolUser
case class Student(name: String, subject: String, section: String, `class`: Int) extends SchoolUser
case class Teacher(name: String, subject: String, age: Int) extends SchoolUser
case class Principal(name: String, year: Int) extends SchoolUser

// School类接收SchoolUser类型的参数,自然限定了user只能是上述三种类型之一
case class School(user: SchoolUser)

2. 实例化方式

现在可以直接创建具体的实体实例,传入School的构造函数:

// 创建包含Principal的School实例
val principal = Principal(name = "ABC", year = 2022)
val schoolWithPrincipal = School(user = principal)

// 创建包含Student的School实例
val student = Student(name = "小明", subject = "数学", section = "A", `class` = 3)
val schoolWithStudent = School(student)

// 创建包含Teacher的School实例
val teacher = Teacher(name = "李老师", subject = "英语", age = 35)
val schoolWithTeacher = School(teacher)

3. Sealed Trait的优势

  • 编译时类型检查:因为SchoolUser是sealed的,编译器会确保所有实现类都在当前文件中,后续无法新增未声明的类型,严格限制School.user的可选范围。
  • 完善的模式匹配支持:对School.user做模式匹配时,编译器会检查是否覆盖了所有可能的类型,避免遗漏分支:
schoolWithPrincipal.user match {
  case Student(n, s, sec, cls) => println(s"学生:$n,班级:$cls")
  case Teacher(n, s, a) => println(s"老师:$n,年龄:$a")
  case Principal(n, y) => println(s"校长:$n,任职年份:$y")
}

4. 为什么不推荐嵌套Either?

Either仅适用于二选一的场景,当需要支持三种及以上类型时,嵌套Either(比如Either[Teacher, Either[Student, Principal]])会让代码结构复杂、可读性下降,而sealed trait可以优雅支持任意数量的类型。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:45:32