scala中的case class

67 阅读3分钟

一、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 的核心特点

  1. 自动生成构造器

    • 无需 new 关键字即可创建实例(编译器自动生成伴生对象的 apply 方法):

      scala

      val stu = Student("1", "小花") // 等价于 Student.apply("1", "小花")
      
  2. 参数默认是 val(不可变)

    • 未显式声明 var 时,参数自动转为类的 val 成员,不可修改(符合函数式编程的不可变理念):

      scala

      stu.id = "2" // 编译报错(val 不可变);若参数是 var 则可修改
      
  3. 自动重写通用方法编译器自动重写 Any 类的核心方法,无需手动实现:

    • toString:返回易读的字符串(如 Student(1,小花),而非对象内存地址);

    • equals:基于所有参数的值比较(而非引用);

    • hashCode:基于所有参数生成哈希值(保证 equals 相等的对象 hashCode 也相等);

    • copy:生成对象的浅拷贝(支持按需修改部分参数):

      scala

      val stu2 = stu.copy(name = "小红") // 拷贝 stu,仅修改 name
      
  4. 支持模式匹配天生适配 Scala 模式匹配,是模式匹配的核心载体:

    scala

    stu match {
      case Student("1", name) => println(s"匹配到ID=1的学生:$name")
      case _ => println("未匹配")
    }
    
  5. 自动生成伴生对象编译器自动为 case class 创建同名伴生对象,包含:

    • apply 方法(简化实例创建);
    • unapply 方法(支持模式匹配的 “解构”)。
  6. 序列化友好默认实现 Serializable,适合分布式场景(如 Spark 中传递 case class)。

三、Case Class 与普通 Class 的核心区别

特性普通 ClassCase 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 更适合 “纯数据” 场景。