清晨六点,快递分拨中心的传送带刚刚启动。分拣员阿凯左手边是仓库的“标准面单盒”——所有包裹统一规格,纸箱侧面整整齐齐印着收件人、地址、物品三栏;右手边是一本《分拣规则手册》,“地址在城东走1号通道、城西走2号通道、生鲜走冷链通道”。阿凯的工作很简单:看一眼面单,翻一页规则,手臂一推,包裹滑进正确的通道。这就是 Scala 里最迷人的一对搭档:样例类(case class)是那个标准面单盒,模式匹配(pattern matching)是那本分拣规则手册。两者天生一对,让数据建模像填面单一样自然,让数据处理变成“照着规则投递”。读完这篇文章,你会理解这对搭档如何让代码既安全又优雅。

岗位关联

对于 AI 从业者,案例类与模式匹配是 Scala 数据建模的基石:无论写 Spark 作业做数据清洗,还是搭建领域模型驱动业务逻辑,这套组合都能让代码结构清晰、编译器帮你提前发现漏洞。

目录
  1. 可视化演示
  2. 样例类定义:标准快递盒
  3. 模式匹配语法:分拣规则手册
  4. 解构与提取:拆箱取物
  5. 密封类层次:规格清单
  6. 样例对象:固定规格的小盒子
  7. 相等性比较:核对货物
  8. copy与产品类型:复印与批量订单
  9. 实战应用场景:分拨中心的一天
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示Scala模式匹配的分支流程、样例类自动解构机制,以及与传统if-else方式的多维度对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

模式匹配分支流程 输入值依次匹配Int/Str/List/Null/Default五种类型 match { case Type => result } Scala模式匹配按顺序尝试每个分支,匹配成功即执行

样例类定义:标准快递盒

快递公司为了高效分拣,把所有包裹统一装进一种标准纸箱,外壁固定印着“收件人、地址、物品”三栏。Scala 里的 case class 就是这个标准纸箱:它把“字段有哪些、类型是什么”一次性写在盒面上,让数据携带者一目了然。

如果不用样例类,用普通 class 定义数据载体,就要手写一堆样板代码:getter、equals、hashCode、toString,冗长还容易写错。而 case class 出现后,编译器化身流水线工人,自动把盒面印好——所有样板方法一次性生成,代码立刻清爽。

此后,每当需要“不会随意改动、一眼能看清内容”的数据载体,就用 case class 这个标准纸箱。分拣、打印、核对,处处省心——这正是数据建模的第一步。

case class  User(name:String,  age:Int)\texttt{case class}\; \text{User}(\text{name}: \texttt{String},\; \text{age}: \texttt{Int})
这一行声明,等价于过去几十行手写样板,同时也向编译器宣告:请把这个类当作标准数据盒来对待。
  1. 样例类关键字case class 告诉编译器“进入标准数据盒生产线”,自动补齐各种样板方法。
    case class\texttt{case class}
  2. 类名 User相当于纸箱型号,标识这个数据盒里装的是“用户”信息。
    User\text{User}
  3. 字段列表冒号后的类型标注每个栏位的数据类型,编译器为每个字段生成只读访问器。
    name:String,  age:Int\text{name}: \texttt{String},\; \text{age}: \texttt{Int}
scala3 行
case class User(name: String, age: Int)// 编译器自动生成: apply / unapply / equals / hashCode / toString / copy// 字段默认 val,不可变

模式匹配语法:分拣规则手册

分拣员阿凯从不逐字读纸条,他扫一眼面单,翻一页规则手册,手臂一推就把包裹送上正确的通道。match 表达式就是这本规则手册。

传统 if-else 要层层嵌套,条件一多就变成“面条代码”。模式匹配把“检查什么”和“做什么”整理成一组平铺的 case 分支,从上到下尝试,命中第一个分支就停下。就像手册里写的:“城东走1号,城西走2号,其他走3号。”

规则书写得越清晰,分拣员判断得越快。代码读起来像读流程图,而不是在密密麻麻的 if 里翻找逻辑。

subject  match  {  case  p1⇒e1;  case  p2⇒e2;  … }\text{subject}\; \texttt{match}\; \{\; \texttt{case}\; p_1 \Rightarrow e_1;\; \texttt{case}\; p_2 \Rightarrow e_2;\; \dots \}
规则手册按书写顺序逐条翻阅,第一个匹配成功的 case 生效,_ 是兜底通配符,if 是守卫条件。
  1. 被匹配对象subject 是要被检查的表达式或值,相当于待分拣的包裹本体。
    subject\text{subject}
  2. match 关键字开启分拣流程,打开规则手册。
    match\texttt{match}
  3. case 分支每一条规则由“模式 + 箭头 + 结果”组成,模式命中就执行箭头右侧的表达式。
    case  pi⇒ei\texttt{case}\; p_i \Rightarrow e_i
  4. 箭头符号=> 表示“那么”——匹配成功后的跳转动作。
    ⇒\Rightarrow
scala5 行
def classify(x: Any): String = x match {  case 0          => "zero"  case n if n > 0 => "positive"  case _          => "other"}

解构与提取:拆箱取物

有时候,分拣员光看面单还不够,要打开纸箱核对内容物。模式匹配里的“解构”就是拆箱动作——它不光判断“是不是这个类型”,还能把样例类里的字段像拆快递一样一件件取出来。

这个能力来自编译器自动生成的 unapply 方法。它像是“构造的逆过程”:构造时把字段装进盒子里,解构时再把字段从盒子里取出来。写法上,模式和构造几乎一模一样。

一次拆箱,全部取出。比起 Java 风格一层层调用 getUser().getName(),解构语法让代码像快递单一样平铺直叙、毫无冗余。

case  C(x1,  x2,  …,  xn)⇒将 xi 绑定为局部变量\texttt{case}\; C(x_1,\; x_2,\; \dots,\; x_n) \Rightarrow \text{将 } x_i \text{ 绑定为局部变量}
模式 C(x1, ..., xn) 会调用样例类的 unapply,把字段按位置绑定到变量,右侧表达式可以直接使用这些变量。
  1. 样例类类型 C被解构的目标类型,比如 User。
    C\text{C}
  2. 变量列表x1 到 xn 按位置接收字段,相当于拆箱后摆放整齐的物件。
    (x1,  x2,  …,  xn)(x_1,\; x_2,\; \dots,\; x_n)
  3. 变量绑定模式一旦命中,这些变量就在右侧表达式中生效,不用再手动调用 getter。
    ⇒  bind\Rightarrow\; \text{bind}
scala4 行
user match {  case User(name, age) if age >= 18 => s"$name 已成年"  case User(name, _)                => s"$name 未成年"}

密封类层次:规格清单

快递公司为了避免分拣员遇到“奇怪的箱子”不知所措,会在墙上贴一张规格清单:本公司只有这三种型号的纸箱。sealed trait 就是这张规格清单。

sealed 关键字告诉编译器:“这个 trait 的所有子类型,只能写在同一个文件里。”于是编译器就像一位较真的仓库主管,记住了全部可能的箱型。当你对密封类型做模式匹配时,如果漏掉某种子类型,编译器会提示:你可能漏了货。

有了规格清单,分拣员闭眼都知道共有几种箱型;任何遗漏都会在开工前被老板(编译器)提醒,而不是等包裹到了客户手里才被发现。

sealed trait  Shape;subtypes={C1,  C2,  …,  Cn}\texttt{sealed trait}\; \text{Shape};\quad \text{subtypes} = \{C_1,\; C_2,\; \dots,\; C_n\}
密封类把子类型集合封闭在一个文件内,编译器据此检查 match 是否穷尽所有情况。
  1. sealed trait声明一个密封特征,规定子类型的范围。
    sealed trait  Shape\texttt{sealed trait}\; \text{Shape}
  2. 子类型集合所有直接子类都在同一源文件中定义,构成一个封闭集合。
    subtypes={C1,C2,…,Cn}\text{subtypes} = \{C_1, C_2, \dots, C_n\}
  3. 穷尽性检查match 时若没有覆盖所有子类型,编译器给出编译警告。
    exhaustive check\text{exhaustive check}
scala4 行
sealed trait Shapecase class Circle(r: Double)    extends Shapecase class Rect(w: Double, h: Double) extends Shapecase class Tri(b: Double, h: Double)  extends Shape

样例对象:固定规格的小盒子

有些包裹很特殊——比如公司的合同专用信封,规格永远只有一种,而且全世界只有一个模板。Scala 里 case object 就是这个特殊小盒子。

object 本身就代表单例,整个程序里只有一个实例。加上 case 关键字后,它自动获得 toString、equals、hashCode、可序列化等能力,还能直接作为模式匹配中的常量模式使用,连参数括号都不需要。

像墙上贴的固定价目表,只有一个版本,永远不会复制出第二个来破坏规则。建模“订单状态”这类固定枚举值,样例对象是首选。

case object  Name  ⇒  全局唯一单例\texttt{case object}\; \text{Name}\; \Rightarrow\; \text{全局唯一单例}
样例对象既具备单例特性,又拥有样例类的自动方法,特别适合表示“状态”“标志”这类不携带参数的固定值。
  1. case object声明一个样例对象,等价于一个没有字段的样例类的单例。
    case object\texttt{case object}
  2. 对象名 Name名字既是对象名,也是类型名,匹配时直接写名字即可。
    Name\text{Name}
  3. 全局唯一JVM 中只有一个实例,身份天然唯一。
    ⇒  singleton\Rightarrow\; \text{singleton}
scala4 行
sealed trait OrderStatuscase object Pending extends OrderStatuscase object Paid    extends OrderStatuscase object Shipped extends OrderStatus

相等性比较:核对货物

当两件包裹摆在面前,阿凯会逐栏核对面单:收件人、地址、物品,全部一致才算同一批货。这就是“结构相等”。

普通 Java 对象用 == 比较的是内存引用,而 Scala 里 == 统一委托给 equals 方法。样例类自动重写了 equals 和 hashCode,默认对每个字段逐一比较,所以两个不同实例只要字段相同就判为相等。

两个盒子只要面单内容一样,就视为同一件货。这让样例类放进 Set 或作为 Map 的键时行为符合直觉,也是数据建模中最常用的相等语义。

a==b    ⟺    a.equals(b)    ⟺    ⋀i=1na.fi=b.fia == b \;\iff\; a.\text{equals}(b) \;\iff\; \bigwedge_{i=1}^{n} a.f_i = b.f_i
样例类的相等性建立在“所有字段逐一相等”之上,与对象在内存中的位置无关。
  1. 等价符号a == b 在 Scala 中编译为调用 a.equals(b)。
    a==b    ⟺    a.equals(b)a == b \;\iff\; a.\text{equals}(b)
  2. equals 方法样例类自动生成的 equals 会比较每个字段。
    equals(b)\text{equals}(b)
  3. 字段合取所有字段相等的结果取逻辑与,任何一个字段不同则整个结果为 false。
    ⋀i=1na.fi=b.fi\bigwedge_{i=1}^{n} a.f_i = b.f_i
scala2 行
case class User(name: String, age: Int)User("Tom", 20) == User("Tom", 20) // true

copy与产品类型:复印与批量订单

客户打来电话:“发票地址要改,其他都不动。”阿凯不会退回整箱货,而是复印一张新面单,只把地址栏涂改,其余栏位原样保留。copy 方法就是这张新面单。

user.copy(age = 31) 返回一个新的 User,age 变成 31,其他字段保持原值,原 user 完全不变——完美契合不可变风格。此外,每个样例类都混入了 Product 特征,提供 productArity、productElement 等方法,让框架可以统一遍历字段。

复印并修改面单,原单保留存档,分拣线照常运转。值得一提的是,Scala 禁止 case class 继承另一个 case class,否则相等性语义会被破坏——这也是为了让“标准盒”型号不混乱。

x.copy(fk=vk)⇒x′s.t.  x′.fk=vk,  x′.fj=x.fj  (j≠k)x.\texttt{copy}(f_k = v_k) \Rightarrow x'\quad \text{s.t.}\; x'.f_k = v_k,\; x'.f_j = x.f_j\;(j \neq k)
copy 基于当前实例生成新实例,只替换指定字段,其余字段保持不变。
  1. 原实例 x调用 copy 的样例类对象,作为模板。
    xx
  2. copy 方法自动生成的方法,接受命名参数 f_k = v_k。
    copy(fk=vk)\texttt{copy}(f_k = v_k)
  3. 新实例 x'返回的新对象,只有第 k 个字段改变,其他字段沿用原值。
    x′.fk=vk,  x′.fj=x.fjx'.f_k = v_k,\; x'.f_j = x.f_j
text1 行
原实例.user.copy(age = 31) → 新实例.User(name = "Tom", age = 31)

实战应用场景:分拨中心的一天

早上八点,分拨中心迎来数千件包裹。阿凯手边是标准纸箱和规则手册,墙上是密封规格清单,流水线高速运转。这正是 Scala 生态中 case class + sealed trait + 模式匹配 的日常:标准数据盒 + 分拣规则 + 编译期保障。

在真实项目里,这套组合最常出现在三个地方。其一,领域建模:用 sealed trait 定义订单状态,用模式匹配统一处理状态流转;其二,Spark 作业:用 case class 建模日志结构,在 map 或 collect 里用模式匹配清洗分流数据;其三,事件驱动架构:每个事件是一个样例类,分发器用模式匹配决定交给哪个处理器。性能上,基于类型的模式匹配会被编译器优化成高效跳转,远比一串字符串比较更快。

标准盒 + 规则书 + 密封清单,让海量数据的分拣既快又有保障——这就是“天生一对”的底气。

对每个事件  e:  match(e)⇒对应处理分支;T(n)=O(n)×O(1)\text{对每个事件}\; e:\; \texttt{match}(e) \Rightarrow \text{对应处理分支};\quad T(n) = O(n) \times O(1)
每一条数据经过一次常数时间的匹配分发,n 条数据的总体耗时近似线性,既有表达力又有性能。
  1. 事件流源源不断送入的数据对象,比如日志、订单、消息。
    events:e1,e2,…,en\text{events}: e_1, e_2, \dots, e_n
  2. match 分发每个事件走一遍模式匹配,命中对应分支。
    match(e)⇒branch\texttt{match}(e) \Rightarrow \text{branch}
  3. 复杂度展开单个分支判断接近常数时间,总体呈线性增长。
    T(n)=O(n)×O(1)=O(n)T(n) = O(n) \times O(1) = O(n)

AI 应用场景

电商订单状态机

阶段:业务数据建模与状态流转

解决的问题:传统用整数常量表示订单状态(0 待支付、1 已支付……),判断逻辑散落各处,新增状态容易漏改,代码可读性差。用 sealed trait 定义订单状态、case object 定义具体状态,再用模式匹配把状态流转集中处理,编译器会提醒所有分支是否穷尽。

具体案例:电商系统里,case class Order(id, items, status: OrderStatus) 作为订单数据盒,order.status match { case Pending => ...; case Paid => ...; case Shipped => ... } 分别触发支付提醒、发货通知、物流更新。新增“已取消”状态时,编译器立刻指出哪些分支需要补全。

Spark 日志清洗与分流

阶段:数据预处理与特征提取

解决的问题:大数据作业中日志往往包含多种类型,用 if-else 判断类型既冗长又易错。用 case class 为每类日志建模,在 Spark 的 map/collect 中使用模式匹配拆解字段,代码像流水线规则一样清晰。

具体案例:用户行为日志包含 ClickEvent、PurchaseEvent、SearchEvent,各定义成一个 case class。event match { case ClickEvent(url, ts) => ...; case PurchaseEvent(pid, amount) => ... } 把不同事件路由到对应统计桶,聚合出点击率、转化率等指标。

AI 预测结果处理

阶段:模型输出后的业务决策

解决的问题:AI 模型返回的预测标签常常是裸字符串,后续 if-else 判断脆弱且容易拼写错误。用 sealed trait 加 case object 把标签建模成强类型枚举,模式匹配让每种标签的处理逻辑一目了然。

具体案例:情感分析服务返回 Positive、Neutral、Negative,在 Scala 网关里用 case Positive => 推送优惠券; case Negative => 转人工客服; case Neutral => 不干预 做差异化响应,逻辑集中且编译期可检查。

Python 演示

python60 行
from dataclasses import dataclass, replacefrom math import pi @dataclass(frozen=True)class Shape:    """模拟 Scala 的 sealed trait Shape"""    pass  @dataclass(frozen=True)class Circle(Shape):    radius: float  @dataclass(frozen=True)class Rect(Shape):    width: float    height: float  @dataclass(frozen=True)class Tri(Shape):    base: float    height: float  def area(shape):    """用类型分派模拟 Scala 的模式匹配"""    if isinstance(shape, Circle):        return pi * shape.radius ** 2    if isinstance(shape, Rect):        return shape.width * shape.height    if isinstance(shape, Tri):        return 0.5 * shape.base * shape.height    raise TypeError("未知形状,密封类不允许这种情况")  def describe(shape):    """模拟 match 的守卫条件与解构"""    if isinstance(shape, Circle):        if shape.radius > 1:            return f"大圆,半径{shape.radius}"        return f"小圆,半径{shape.radius}"    if isinstance(shape, Rect):        return f"矩形 {shape.width}x{shape.height}"    if isinstance(shape, Tri):        return f"三角形 底{shape.base} 高{shape.height}"    raise TypeError("未知形状")  if __name__ == "__main__":    shapes = [Circle(radius=2.0), Rect(width=3.0, height=4.0), Tri(base=4.0, height=5.0)]    for s in shapes:        print(describe(s), "面积=%.2f" % area(s))     c1 = Circle(radius=1.0)    c2 = Circle(radius=1.0)    print("结构相等:", c1 == c2)    c3 = replace(c1, radius=3.0)    print("copy 后:", c3)
目的:用 Python 的 dataclass 模拟 Scala 样例类和模式匹配,演示类型分派、结构相等、copy 三个核心特性。
  1. @dataclass(frozen=True)对应 Scala 样例类的不可变参数,生成后又不能被修改。
  2. class Circle(Shape)对应密封 trait 的子类,把类型体系收拢在一个继承关系下。
  3. def area(shape)对应“模式匹配分拣函数”,按类型决定处理逻辑。
  4. isinstance(shape, Circle)对应 case Circle(...) 分支的运行时类型检查。
  5. if shape.radius > 1对应模式匹配中的守卫条件 if radius > 1。
  6. replace(c1, radius=3.0)等价于 Scala 样例类的 copy 方法,返回改字段后的新对象。
  7. c1 == c2对应样例类结构相等的比较语义。

常见误区

误区我一直以为 case class 和普通 class 一样可以互相继承,这样能复用公共字段,少写很多重复代码。我试过让一个 case class extends 另一个 case class,代码能编译过吗?

正解Scala 明确禁止 case-to-case 继承。原因是两个样例类的 equals/hashCode 实现以类名为基础,子类继承父类的相等性会破坏对称性——父类说相等、子类说不相等,集合操作就乱了。就像两种不同型号的标准面单硬拼成一张,核对货物时自相矛盾。

为何常见:从 Java 的“万物皆可继承”惯性思维出发,容易忽略样例类在相等性上的特殊设计。

误区我觉得模式匹配只是把 if-else 换个写法,编译器最终会翻译成一系列 if 判断,本质上没区别。

正解模式匹配远不止相等比较。它可以结构化解构字段、绑定变量、结合密封类做穷尽性检查,编译器还会针对类型匹配生成高效的跳转表或类型标签分派,性能常常优于手写 if 链。分拣员不是挨个翻规则本,而是扫一眼标签直接投递。

为何常见:初学时只看到语法表面相似,没有体会到解构提取和穷尽检查这些深层能力。

误区case object 就是多写一个 case 的 object,功能完全一样,肯定是编译器偷懒。

正解case object 自动获得 Product、Serializable 等特质,toString 更友好,相等性基于对象身份且行为更统一,还能直接作为模式匹配的常量模式使用。普通 object 只是单例,没有这套“标准盒”待遇。

为何常见:只看到关键字多了一个,忽略了编译器背后生成的配套方法。

误区Java 里 `==` 比较引用,所以在 Scala 里 `User("Tom") == User("Tom")` 应该也是 false,因为 new 了两次。

正解Scala 的 == 统一委托给 equals,样例类自动重写了 equals 和 hashCode,按字段逐一比较。两个不同实例只要字段相同就相等。比较的是面单内容,而不是盒子在仓库里的物理位置。

为何常见:从 Java 迁移到 Scala 时,最容易沿用的“== 即引用”的思维惯性。

误区sealed 好像只是个修饰词,写了不写都能运行,也许只是给读代码的人看的文档说明。

正解sealed 有实际编译期效果:它限制所有子类必须位于同一源文件,并让编译器在做模式匹配时执行穷尽性检查。漏掉某个子类型会收到编译警告,相当于老板拿着规格清单逐条检查你的分拣操作。

为何常见:因为 sealed 本身不改变运行行为,效果要到对密封类型做 match 时才显现出来。

面试题

Q1. Scala 的样例类会自动生成一系列方法,以下哪个不是自动生成的?

单选题 · easy

  • A.apply 工厂方法
  • B.equals 判等方法
  • C.单例获取方法← 正确答案
  • D.copy 复制方法

答案:C。样例类自动生成 apply、unapply、equals、hashCode、toString、copy 等方法。C 选项的“单例获取方法”(如 getInstance)属于手写单例模式的产物,样例类并不自动生成。

Q2. 模式匹配中 case _ => ... 里的 _ 表示什么?

单选题 · easy

  • A.匹配任意值不绑定← 正确答案
  • B.只匹配整数常量
  • C.匹配字符串空值
  • D.匹配方法调用值

答案:A。_ 是通配符,匹配任意值且不绑定变量,通常放在最后作为兜底分支。其他选项都错误地限定了匹配范围。

Q3. 关于 sealed trait(密封特征)的作用,正确的有哪些?

多选题 · mid

  • A.限制子类只能在同一文件← 正确答案
  • B.帮助编译器穷尽检查← 正确答案
  • C.运行期自动提升性能
  • D.强制子类使用样例类

答案:AB。sealed trait 把子类型限定在同一源文件内,使编译器能枚举全部子类型并做穷尽性检查,防止 match 漏分支。它本身不改变运行期性能,也不强制子类必须是样例类。

Q4. 下列哪个声明能同时具备单例与样例类的自动方法?

单选题 · easy

  • A.case class 声明
  • B.case object 声明← 正确答案
  • C.class 普通声明
  • D.trait 密封声明

答案:B。case object 既是单例,又自动获得样例类的 toString、equals、hashCode、序列化等方法。case class 具备自动方法但不是单例,普通 class 和 trait 都不具备这些能力。

Q5. User("Tom", 20) == User("Tom", 20) 的结果是?

单选题 · mid

  • A.结果是 false 引用不同
  • B.结果是 true 结构相同← 正确答案
  • C.编译器直接报错
  • D.结果取决于运行环境

答案:B。样例类自动重写 equals,按所有字段逐一比较。两个实例字段都相同,因此返回 true。普通class 默认引用比较,但样例类走结构相等。

Q6. user.copy(age = 31) 执行后会发生什么?

单选题 · mid

  • A.user 的 age 字段被改为 31
  • B.生成新对象,age 为 31← 正确答案
  • C.程序运行时抛出异常
  • D.返回 user 的引用本身

答案:B。copy 基于当前实例生成新实例,只替换指定字段,原实例保持不变,符合不可变风格。由于字段默认是 val,不可能原地修改。

Q7. 关于模式匹配,下列哪些说法正确?

多选题 · mid

  • A.case 分支按顺序尝试← 正确答案
  • B.可以解构样例类字段← 正确答案
  • C.通配符只能出现一次
  • D.每个 case 必须有守卫

答案:AB。case 分支从上到下依次尝试,第一个命中的分支生效;模式匹配可以调用 unapply 解构样例类字段。通配符并非只能出现一次,守卫条件也可选,因此 C、D 错误。

Q8. sealed trait Shape 有 Circle、Rect、Tri 三个子类,match 时漏写 case Tri 会怎样?

单选题 · hard

  • A.编译错误直接中断
  • B.编译警告可能不穷尽← 正确答案
  • C.运行时忽略 Tri 数据
  • D.自动匹配到默认分支

答案:B。Scala 对密封类型的 match 做穷尽性检查,漏写子类会得到 "match may not be exhaustive" 编译警告(不是硬错误)。运行时如果真传入 Tri,会抛出 MatchError,不会自动忽略或匹配默认分支。

Q9. 关于样例类继承,下列哪些说法正确?

多选题 · hard

  • A.样例类可以继承普通类← 正确答案
  • B.样例类可以实现多个 trait← 正确答案
  • C.两个样例类可以互相继承
  • D.样例类继承样例类破坏相等性← 正确答案

答案:ABD。Scala 允许样例类继承普通类(条件较严格)和实现 trait,但禁止 case-to-case 继承,因为会破坏 equals 的对称性和一致性。因此 A、B、D 正确,C 错误。

Q10. 模式匹配中 case User(name, age) 中的 name 和 age 是什么?

单选题 · mid

  • A.两个字符串常量
  • B.绑定变量提取字段← 正确答案
  • C.样例类的类型参数
  • D.两个无意义占位符

答案:B。模式中的 name 和 age 是绑定变量,匹配成功后从样例类实例的字段中取值并绑定,右侧表达式可以直接使用它们。这是模式匹配解构能力的体现。