一、Case Class 的定义
Scala 中的 case class 是一种特殊的类,专为不可变数据建模、模式匹配等场景设计,定义语法简洁,核心格式:
scala
// 基本定义(参数默认是 val,不可变)
case class 类名(参数1: 类型1, 参数2: 类型2, ...)
// 示例:定义一个表示学生的 case class
case class Student(id: String, name: String)
补充说明:
-
若需要可变参数,需显式声明
var(不推荐,违背不可变设计初衷):scala
case class Student(var id: String, var name: String) // 可变参数 -
可继承(但建议作为 “数据载体”,避免复杂继承),也可定义方法 / 重写方法:
scala
case class Student(id: String, name: String) { def fullInfo: String = s"ID: $id, Name: $name" // 自定义方法 }
二、Case Class 的核心特点
-
自动生成构造器
-
无需
new关键字即可创建实例(编译器自动生成伴生对象的apply方法):scala
val stu = Student("1", "小花") // 等价于 Student.apply("1", "小花")
-
-
参数默认是 val(不可变)
-
未显式声明
var时,参数自动转为类的val成员,不可修改(符合函数式编程的不可变理念):scala
stu.id = "2" // 编译报错(val 不可变);若参数是 var 则可修改
-
-
自动重写通用方法编译器自动重写
Any类的核心方法,无需手动实现:-
toString:返回易读的字符串(如Student(1,小花),而非对象内存地址); -
equals:基于所有参数的值比较(而非引用); -
hashCode:基于所有参数生成哈希值(保证equals相等的对象hashCode也相等); -
copy:生成对象的浅拷贝(支持按需修改部分参数):scala
val stu2 = stu.copy(name = "小红") // 拷贝 stu,仅修改 name
-
-
支持模式匹配天生适配 Scala 模式匹配,是模式匹配的核心载体:
scala
stu match { case Student("1", name) => println(s"匹配到ID=1的学生:$name") case _ => println("未匹配") } -
自动生成伴生对象编译器自动为
case class创建同名伴生对象,包含:apply方法(简化实例创建);unapply方法(支持模式匹配的 “解构”)。
-
序列化友好默认实现
Serializable,适合分布式场景(如 Spark 中传递 case class)。
三、Case Class 与普通 Class 的核心区别
| 特性 | 普通 Class | Case Class |
|---|---|---|
| 实例创建 | 必须用 new 关键字 | 无需 new(自动生成 apply 方法) |
| 参数默认修饰符 | 无(需显式声明 val/var) | 默认 val(不可变) |
equals 方法 | 基于引用比较(需手动重写) | 基于参数值比较(自动重写) |
toString 方法 | 返回内存地址(需手动重写) | 返回易读的参数字符串(自动重写) |
hashCode 方法 | 基于对象引用(需手动重写) | 基于参数值生成(自动重写) |
copy 方法 | 无(需手动实现) | 自动生成(支持浅拷贝 + 部分参数修改) |
| 模式匹配 | 不支持(需手动实现 unapply) | 原生支持(自动生成 unapply) |
| 伴生对象 | 需手动创建 | 自动生成(含 apply/unapply) |
| 序列化 | 需手动继承 Serializable | 自动实现 Serializable |
实战对比(基于你提供的代码)
普通 Class 版本(你的代码) :
scala
class Student(var id:String, var name:String) {
// 手动重写 equals 才能实现值比较
override def equals(obj: Any): Boolean = {
val other = obj.asInstanceOf[Student]
other.id == id && other.name == name
}
// 手动重写 toString 才能易读
override def toString: String = s"Student($id, $name)"
}
val stu1 = new Student("1", "小花")
val stu2 = new Student("1", "小花")
println(stu1 == stu2) // true(但需手动重写 equals)
val set1 = Set(stu1, stu2)
println(set1) // Set(Student(1, 小花), Student(1, 小花))
// ❶ 普通 Class 未重写 hashCode,Set 认为是两个对象(即使 equals 相等)
// ❷ 若只重写 equals 不重写 hashCode,违反 "equals 相等则 hashCode 必须相等" 规则
Case Class 版本(简化版) :
scala
case class Student(id: String, name: String) // 无需手动重写任何方法
val stu1 = Student("1", "小花") // 无需 new
val stu2 = Student("1", "小花")
println(stu1 == stu2) // true(自动值比较)
val set1 = Set(stu1, stu2)
println(set1) // Set(Student(1, 小花))(hashCode 一致,Set 去重)
关键结论:
你的普通 Class 代码中,Set(stu1, stu2) 会输出两个元素(因为未重写 hashCode),而换成 Case Class 后,Set 会自动去重(hashCode 基于参数值生成,stu1/stu2 的 hashCode 相等)。
四、Case Class 的使用场景
- 数据载体(如 DTO、VO、领域模型);
- 模式匹配场景(如处理消息、解析数据);
- 分布式 / 序列化场景(如 Spark/Flink 中的数据传输);
- 不可变数据建模(函数式编程风格)。
注意:若需要可变状态、复杂业务逻辑(如大量方法),建议用普通 Class;Case Class 更适合 “纯数据” 场景。