当前位置: 首页 > news >正文

七、Scala 包、样例类与样例对象

随着我们构建的系统越来越复杂,如何清晰地组织代码高效地数据进行建模,变得至关重要。本节,我们将深入学习 Scala 提供的两大利器:用于构建命名空间模块化,以及为数据而生、并为模式匹配提供强大支持样例类样例对象

思维导图

在这里插入图片描述
在这里插入图片描述

一、包

1.1 包简介

是 Scala 组织代码基本单位。它提供了一个命名空间,可以有效避免不同代码模块间的命名冲突,并帮助我们构建清晰的项目结构

1.2 包的定义格式

Scala 支持两种定义包的方式:

方式一:文件顶部声明
这是最常见的方式,与 Java 类似。

package com.mycompany.project.moduleAclass MyClass {// ...
}

方式二:嵌套风格
允许在同一个文件定义属于不同包的内容,并共享父包的作用域

package com.mycompany.project {// 这里的代码属于 com.mycompany.project 包package moduleB {// 这里的代码属于 com.mycompany.project.moduleB 包class AnotherClass {// ...}}
}

1.3 作用域

使用嵌套风格时,子包可以直接访问父包中的成员无需导入

package parent {class ParentClasspackage child {class ChildClass {val p = new ParentClass //可以直接访问}}
}

1.4 包的引入

使用 import 关键字可以引入其他包中的类、对象或成员,以便在当前文件直接使用

引入方式语法示例与说明
引入单个成员import path.to.Memberimport java.util.Date
引入多个成员import path.to.{Member1, Member2}import scala.collection.mutable.{Map, Set}
通配符引入import path.to._import scala.collection.mutable._ (引入 mutable 包下所有成员)
重命名引入import path.to.{Member => NewName}import java.util.{Date => JDate} (避免与自定义的Date类冲突)
隐藏成员引入import path.to.{Member => _, _}import java.util.{Date => _, _} (引入 util 包下除 Date 外的所有成员)

1.5 包对象

有时,我们希望在整个包的范围内共享一些常量或工具方法包对象就是为此而生。每个包最多只能有一个包对象。

语法:
在与包同级的目录下,创建一个名为 package.scala 的文件。

// 文件名:com/mycompany/project/package.scala
package com.mycompanypackage object project {val PI = 3.14159def sayPackageHello(): Unit = println("Hello from the project package!")
}

使用:

// 在 com.mycompany.project 包下的任何文件中
package com.mycompany.projectclass MyClass {def calculate(radius: Double): Double = {sayPackageHello() // 直接调用2 * PI * radius   // 直接使用}
}

1.6 包的可见性

可以使用 private[包名] 修饰符扩大私有成员的可见范围,使其在指定的包内可见。

二、样例类

样例类是一种特殊的类,它专门为存储不可变 (immutable)数据优化。当你定义一个类为 case class 时,Scala 编译器会自动为你生成一系列实用方法

2.1 格式与特性

特性 (Feature)编译器自动生成的内容说明
不可变性主构造器参数默认为 val鼓励使用不可变的数据模型,更安全
工厂方法伴生对象中自动创建 apply 方法创建实例时无需使用 new 关键字,代码更简洁。
模式匹配支持伴生对象中自动创建 unapply 方法这是样例类最重要的特性,使其能无缝用于模式匹配。
自动方法实现实现了合理的 toString, equals, hashCode 方法。便于打印比较和在集合中使用。
copy 方法自动创建 copy 方法可以方便地创建一个新实例,并修改其中部分字段

2.2 示例

// 定义一个样例类
case class Person(name: String, age: Int)// 1. 无需 new 关键字创建实例 (apply 方法)
val alice = Person("Alice", 30)
val bob = Person("Bob", 32)
val anotherAlice = Person("Alice", 30)// 2. 友好的 toString 输出
println(alice) // 输出: Person(Alice,30)// 3. 基于值的 equals 和 hashCode
println(alice == bob) // 输出: false
println(alice == anotherAlice) // 输出: true// 4. 使用 copy 方法创建新实例
val olderAlice = alice.copy(age = 31)
println(olderAlice) // 输出: Person(Alice,31)

2.3 样例类中的默认方法
如上表所示,apply, unapply, toString, equals, hashCode, copy 等都是编译器为样例类自动生成“默认方法”

三、样例对象

3.1 格式
case object 是一个同时具备样例类特性和单例对象特性的特殊对象

3.2 示例
样例对象非常适合用于表示枚举固定状态的集合,通常与 sealed trait (密封特质) 配合使用。

// 密封特质,表示其所有子类必须在同一个文件中定义
sealed trait DayOfWeekcase object Monday extends DayOfWeek
case object Tuesday extends DayOfWeek
// ... 其他天def activity(day: DayOfWeek): String = day match {case Monday => "Work hard"case _ => "Relax" // _ 是通配符
}println(activity(Monday)) // 输出: Work hard

四、综合案例

这个案例将综合运用本节的知识,特别是样例类和样例对象,来构建一个灵活的消息模型

8.1 需求
我们需要一个系统来表示不同类型的消息,例如文本消息图片消息,以及一个特殊断开连接信号。

8.2 目的
使用 sealed trait, case classcase object 来清晰地这些消息进行建模,并编写一个可以处理不同消息类型的函数

8.3 参考代码

package com.example.messaging// 1. 使用 sealed trait 定义消息的基类型
sealed trait Message// 2. 使用 case class 定义包含数据的消息类型
case class TextMessage(sender: String, content: String) extends Message
case class ImageMessage(sender: String, imageUrl: String, caption: String) extends Message// 3. 使用 case object 定义不包含数据的信号类型消息
case object Disconnect extends Message// 4. 创建一个消息处理器对象
object MessageProcessor {// 这个方法使用模式匹配来处理不同类型的消息// (模式匹配是后续章节的核心内容,这里是预演)def process(msg: Message): String = msg match {case TextMessage(sender, content) =>s"Received text from $sender: '$content'"case ImageMessage(sender, url, caption) =>s"Received an image from $sender with caption '$caption'. URL: $url"case Disconnect =>"A client has disconnected."}
}// 5. 在主程序中使用
object MainApp extends App {import com.example.messaging._val text = TextMessage("Alice", "Hello there!")val image = ImageMessage("Bob", "http://example.com/img.png", "My vacation photo")val disconnectSignal = Disconnectprintln(MessageProcessor.process(text))println(MessageProcessor.process(image))println(MessageProcessor.process(disconnectSignal))
}

练习题

题目一:包定义
在一个名为 MyApp.scala 的文件中,使用嵌套风格DatabaseService 类定义在 com.myapp.services 包下,将 User 类定义在 com.myapp.models 包下。

题目二:包引入
在一个文件中,如何只引入 scala.collection.mutable 包中的 ArrayBufferListBuffer

题目三:重命名引入
你的代码中已经有一个 Map 类,现在你需要使用 scala.collection.immutable.Map。请写出引入语句,将 scala.collection.immutable.Map 重命名为 ImmutableMap避免冲突

题目四:包对象
com.utils 包中创建一个包对象,定义一个名为 APP_VERSION 的常量,值为 "1.0.0"

题目五:简单样例类
定义一个 case class Point,它有两个 Double 类型的字段:xy

题目六:样例类实例化
使用上题定义的 Point 样例类,创建一个实例 p1,表示坐标 (3.0, 4.0),不要使用 new 关键字。

题目七:样例类的 copy 方法
基于上题创建的 p1 实例,使用 copy 方法创建一个新实例 p2,其 x 坐标与 p1 相同,但 y 坐标为 -4.0

题目八:样例类的 equals
创建两个 Point 实例,p3p4,它们都表示坐标 (1.5, 2.5)。然后判断 p3 == p4 的结果是什么。

题目九:样例对象
使用 sealed traitcase object 定义一个 Status 枚举,包含三个状态:Pending, Success, Failure

题目十:在样例类中使用 var
定义一个 case class Task,它有一个不可变id (Int) 和一个可变status (String)。

题目十一:包可见性
com.security 包中定义一个 Credentials 类。该类有一个 password 字段,使其只能在 com.security内部可见。

题目十二:unapply 方法的作用
样例类自动生成的 unapply 方法最主要的用途是什么?

题目十三:样例对象与样例类的区别
case classcase object 最根本的区别是什么?

题目十四:综合 - HTTP请求模型
使用 sealed trait 和样例类/对象为简单的HTTP请求方法进行建模,包括 GET(url: String), POST(url: String, body: String) 和一个表示 OPTIONS 请求的样例对象。

题目十五:综合 - 几何图形模型
定义一个 sealed trait GeometricShape。然后定义两个 case classCircle(radius: Double)Rectangle(width: Double, height: Double),都继承自 GeometricShape

答案与解析

答案一:

package com.myapp {package models {class User}package services {class DatabaseService}
}

解析: 嵌套包定义允许在同一文件中组织多个相关包的类。

答案二:

import scala.collection.mutable.{ArrayBuffer, ListBuffer}

解析: 使用花括号 {...} 可以从同一个父包中选择性地引入多个成员。

答案三:

import scala.collection.immutable.{Map => ImmutableMap}

解析: => 符号用于在引入时为成员指定一个新的名称。

答案四:

// 文件路径: com/utils/package.scala
package compackage object utils {val APP_VERSION = "1.0.0"
}

解析: 包对象必须定义在名为 package.scala 的文件中。

答案五:

case class Point(x: Double, y: Double)

解析: 这是定义样例类的最简洁形式。xy 默认是 public val

答案六:

val p1 = Point(3.0, 4.0)

解析: 样例类会自动生成伴生对象和 apply 方法,因此可以省略 new

答案七:

val p1 = Point(3.0, 4.0)
val p2 = p1.copy(y = -4.0)

解析: copy 方法通过带名参数可以方便地创建修改了部分字段的新实例。

答案八:
结果是 true

val p3 = Point(1.5, 2.5)
val p4 = Point(1.5, 2.5)
println(p3 == p4) // 输出: true

解析: 样例类的 equals 方法是基于值的比较,而不是引用比较。只要所有字段的值都相等,实例就相等。

答案九:

sealed trait Status
case object Pending extends Status
case object Success extends Status
case object Failure extends Status

解析: sealed trait + case object 是在 Scala 2 中实现类型安全枚举的标准模式。

答案十:

case class Task(val id: Int, var status: String)

解析: 可以在样例类的构造器参数前显式使用 var 来创建可变字段。

答案十一:

package com.security {class Credentials {private[security] var password: String = _}class Authenticator {def check(c: Credentials): Unit = {println(c.password) // 在 com.security 包内可以访问}}
}

解析: private[security]password 的可见性限定在 com.security 包内。

答案十二:
unapply 方法最主要的用途是支持模式匹配

解析: 它允许样例类实例被“解构”,即在 case 语句中提取出其构造器参数。

答案十三:
最根本的区别是:case class 是一个,用于创建多个实例;而 case object 是一个单例对象全局只有一个实例。

答案十四:

sealed trait HttpMethod
case class GET(url: String) extends HttpMethod
case class POST(url: String, body: String) extends HttpMethod
case object OPTIONS extends HttpMethod

解析: 这个模型清晰地区分了需要携带数据的请求 (GET, POST) 和不需要携带数据的信号类请求 (OPTIONS)。

答案十五:

sealed trait GeometricShape
case class Circle(radius: Double) extends GeometricShape
case class Rectangle(width: Double, height: Double) extends GeometricShape

解析: sealed traitcase class 结合是构建代数数据类型 (ADT) 的基础,在函数式编程中非常常见。

在这里插入图片描述

日期:2025年9月21日
专栏:Scala教程

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=480jfj75dvu

http://www.dtcms.com/a/394466.html

相关文章:

  • CSP - 2025 普及组初赛试题及解析
  • Matlab实现点云的体素下采样
  • 淘宝 item_search_img(拍立淘)API 接口获取与应用指南
  • Python网络请求库requests使用详述
  • B站 弹幕 相关工具
  • 23 webUI应用基础案例-线稿上色
  • 【MicroPython编程】-深入了解MicroPython 的垃圾收集
  • STM32F429I-DISC1【板载LED呼吸灯】
  • OBOO鸥柏工业触摸屏:信创国产化芯片驱动,展现军工级卓越性能
  • Ubantu命令行指令大全
  • 字节面试题:正则化技术如何影响网络梯度
  • Java进阶教程,全面剖析Java多线程编程,死锁,笔记15
  • 【含文档+PPT+源码】基于SpringBoot+Vue的车牌识别实时交通流量统计系统
  • C++动态规划4
  • chmod命令
  • kernel 6.6中新增的EEVDF特性
  • MATLAB M代码解释器设计与C++实现
  • nivida jetson orinnx torch环境搭建
  • Java进阶教程,全面剖析Java多线程编程,线程的生命周期,笔记11
  • Javase 基础加强 —— 12 网络编程
  • 【04】EPGF 架构搭建教程之 工具环境变量的配置
  • Oracle -运维学习路线 --学习篇1
  • 三个余弦:平方和凑成1时会发生什么
  • 碧蓝航线装备参数探究——关于金色之耻
  • Golang圖書館
  • linux命令--迭代积累
  • Unity2D-物理系统
  • 崩铁 预言算牌 解谜
  • 解锁AI巨型模型训练:DeepSpeed ZeRO Offload 全面指南
  • python语言中的常用容器(集合)