Hadoop学习教程,从入门到精通, Scala语言 — 完整知识点详解(13)

Scala语言 --- 完整知识点详解


一、什么是Scala

Scala(Scalable Language) 是一门多范式编程语言,融合了面向对象编程函数式编程的特性。

核心特点:

  • 运行在JVM(Java虚拟机)上,与Java完全兼容
  • 支持面向对象编程(类、特质、继承、多态)
  • 支持函数式编程(高阶函数、不可变数据、模式匹配)
  • 静态类型系统,类型推断强大
  • 语法简洁,代码量通常比Java少很多
  • 是Apache Spark的原生开发语言

二、安装Scala

2.1 前提条件:安装JDK

Scala运行在JVM上,必须先安装JDK 8或以上版本。

验证JDK安装:

bash 复制代码
# 打开终端/命令行,输入以下命令查看Java版本
java -version
# 如果显示类似 "1.8.0_xxx" 的版本信息,说明JDK已安装

2.2 Windows中安装Scala

步骤一:下载Scala安装包
  1. 打开浏览器,访问Scala官方下载页面:https://www.scala-lang.org/download/
  2. 推荐下载 Scala 2.12.xScala 2.11.x(与Spark版本对应)
  3. 下载 .msi 格式的Windows安装包(如 scala-2.12.18.msi
步骤二:运行安装程序
  1. 双击下载的 .msi 安装文件
  2. 点击 Next 进入安装向导
  3. 阅读许可协议,勾选 I accept the terms ,点击 Next
  4. 选择安装路径,建议使用默认路径:C:\Program Files (x86)\scala\
  5. 点击 Install 开始安装
  6. 等待安装完成后,点击 Finish
步骤三:配置环境变量
  1. 右键点击 此电脑(我的电脑)属性高级系统设置

  2. 点击 环境变量 按钮

  3. 系统变量 区域,找到并编辑 Path 变量,添加:

    复制代码
    C:\Program Files (x86)\scala\bin
  4. 新建系统变量:

    • 变量名:SCALA_HOME
    • 变量值:C:\Program Files (x86)\scala
步骤四:验证安装
bash 复制代码
# 打开命令提示符(CMD),输入以下命令
scala -version
# 正常输出:Scala code runner version 2.12.18

# 进入Scala交互式命令行(REPL)
scala
# 出现 scala> 提示符即表示安装成功
# 输入 :quit 退出

2.3 CentOS 7中安装Scala

方法一:使用RPM包安装
bash 复制代码
# 步骤1:下载Scala RPM安装包
# 使用wget命令下载Scala 2.12.18的RPM包
wget https://downloads.lightbend.com/scala/2.12.18/scala-2.12.18.rpm

# 步骤2:使用rpm命令安装Scala
# -ivh 参数:i=安装, v=显示详细信息, h=显示进度条
sudo rpm -ivh scala-2.12.18.rpm

# 步骤3:验证安装
# 查看Scala版本信息
scala -version
方法二:使用压缩包手动安装(推荐)
bash 复制代码
# 步骤1:下载Scala压缩包
# 使用wget下载tgz格式的压缩包
wget https://downloads.lightbend.com/scala/2.12.18/scala-2.12.18.tgz

# 步骤2:解压到指定目录
# -z:使用gzip解压, -x:解压, -v:显示过程, -f:指定文件
# -C:指定解压目标目录为 /usr/local/
sudo tar -zxvf scala-2.12.18.tgz -C /usr/local/

# 步骤3:进入解压目录,重命名为scala(方便后续配置)
cd /usr/local/
sudo mv scala-2.12.18 scala

# 步骤4:配置环境变量
# 编辑 /etc/profile 文件,在文件末尾添加Scala环境变量
sudo vi /etc/profile

# 在文件末尾添加以下两行内容:
# export SCALA_HOME=/usr/local/scala
# export PATH=$SCALA_HOME/bin:$PATH

# 步骤5:使环境变量配置立即生效
source /etc/profile

# 步骤6:验证安装
# 查看Scala版本
scala -version

# 进入Scala交互式命令行
scala
# 出现 scala> 提示符表示安装成功
# 输入 :quit 退出Scala REPL

三、Scala基础

3.1 变量声明

Scala中使用 valvar 两种关键字声明变量:

  • val(不可变变量) :声明后值不能修改,类似Java中的 final
  • var(可变变量):声明后值可以修改

基本语法:

scala 复制代码
// val 变量名: 数据类型 = 初始值
// var 变量名: 数据类型 = 初始值

object VariableDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== val 不可变变量 ====================
    
    // 使用val声明一个不可变变量name,类型为String
    // Scala会自动推断类型,可以省略": String"
    val name: String = "张三"
    println("姓名:" + name)  // 输出:姓名:张三
    
    // 如果尝试修改val变量,编译会报错
    // name = "李四"   // 错误:Reassignment to val(不允许重新赋值)
    
    // 省略类型声明,Scala自动推断为Int类型
    val age = 25
    println("年龄:" + age)  // 输出:年龄:25
    
    // ==================== var 可变变量 ====================
    
    // 使用var声明一个可变变量score,类型为Int
    var score: Int = 90
    println("初始分数:" + score)   // 输出:初始分数:90
    
    // var变量可以修改值
    score = 95
    println("修改后分数:" + score) // 输出:修改后分数:95
    
    // ==================== 同时声明多个变量 ====================
    
    // 使用逗号分隔,同时声明多个变量
    val (x, y, z) = (1, 2, 3)
    println(s"x=$x, y=$y, z=$z")   // 输出:x=1, y=2, z=3
    
    // ==================== 懒加载变量 ====================
    
    // lazy修饰的变量在第一次使用时才进行初始化(惰性求值)
    lazy val message = "我是懒加载变量"
    println(message)  // 第一次使用时才初始化,输出:我是懒加载变量
  }
}

类型推断示例:

scala 复制代码
object TypeInferenceDemo {
  def main(args: Array[String]): Unit = {
    
    // Scala编译器会根据右侧的值自动推断变量类型
    
    // 推断为Int类型
    val num = 100          // 等价于 val num: Int = 100
    
    // 推断为Double类型(包含小数点的数值默认为Double)
    val pi = 3.14          // 等价于 val pi: Double = 3.14
    
    // 推断为String类型
    val greeting = "hello" // 等价于 val greeting: String = "hello"
    
    // 推断为Boolean类型
    val flag = true        // 等价于 val flag: Boolean = true
    
    // 推断为Char类型(使用单引号)
    val ch = 'A'           // 等价于 val ch: Char = 'A'
    
    // 需要显式指定类型的情况:当编译器无法准确推断时
    // 例如需要声明为Long类型,但初始值在Int范围内
    val bigNum: Long = 100  // 如果不加: Long,会推断为Int
    
    // 需要声明为Float类型
    val f: Float = 3.14f    // 不加: Float 会推断为Double
    
    println(s"num=$num, pi=$pi, greeting=$greeting")
    println(s"flag=$flag, ch=$ch, bigNum=$bigNum, f=$f")
  }
}

3.2 数据类型

Scala的数据类型与Java类似,但所有数据类型都是对象(Scala中没有Java的基本数据类型)。

数据类型 说明 取值范围/示例
Byte 8位有符号整数 -128 ~ 127
Short 16位有符号整数 -32768 ~ 32767
Int 32位有符号整数 -2^31 ~ 2^31-1
Long 64位有符号整数 -2^63 ~ 2^63-1
Float 32位浮点数 1.23f
Double 64位浮点数 3.14
Char 16位Unicode字符 'A'
String 字符串 "hello"
Boolean 布尔值 true / false
Unit 表示无值,类似void ()
Null null引用 null
Nothing 所有类型的子类型 无实例
Any 所有类型的超类型 -
AnyVal 值类型的超类型 -
AnyRef 引用类型的超类型 -

类型层次结构:

复制代码
              Any
            /    \
       AnyVal    AnyRef (java.lang.Object)
      /  |  \        |
   Int Double Char  String, List, ...
      \    |    /
       Nothing

案例代码:

scala 复制代码
object DataTypeDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 整数类型 ====================
    
    // Byte类型:8位,范围 -128 到 127
    val byteVal: Byte = 127
    
    // Short类型:16位,范围 -32768 到 32767
    val shortVal: Short = 32767
    
    // Int类型:32位,最常用的整数类型
    val intVal: Int = 2147483647
    
    // Long类型:64位,需要在数字后加 L 或 l
    val longVal: Long = 9223372036854775807L
    
    println(s"Byte: $byteVal")    // 输出:Byte: 127
    println(s"Short: $shortVal")  // 输出:Short: 32767
    println(s"Int: $intVal")      // 输出:Int: 2147483647
    println(s"Long: $longVal")    // 输出:Long: 9223372036854775807
    
    // ==================== 浮点类型 ====================
    
    // Float类型:32位浮点数,需要在数字后加 F 或 f
    val floatVal: Float = 3.14f
    
    // Double类型:64位浮点数,默认的小数类型
    val doubleVal: Double = 3.141592653589793
    
    println(s"Float: $floatVal")    // 输出:Float: 3.14
    println(s"Double: $doubleVal")  // 输出:Double: 3.141592653589793
    
    // ==================== 字符类型 ====================
    
    // Char类型:单引号包裹的单个字符
    val charVal: Char = 'A'
    val chineseVal: Char = '中'    // Scala的Char支持Unicode字符
    println(s"Char: $charVal")      // 输出:Char: A
    println(s"中文字符: $chineseVal") // 输出:中文字符: 中
    
    // ==================== 布尔类型 ====================
    
    // Boolean类型:只有true和false两个值
    val boolVal: Boolean = true
    println(s"Boolean: $boolVal")   // 输出:Boolean: true
    
    // ==================== 字符串类型 ====================
    
    // String类型:双引号包裹的字符串
    val strVal: String = "Hello Scala"
    println(s"String: $strVal")     // 输出:String: Hello Scala
    
    // 多行字符串:使用三引号 """ """ 包裹
    val multiLine: String = """这是第一行
这是第二行
这是第三行"""
    println(multiLine)
    
    // 字符串插值:s前缀 + $变量名 或 ${表达式}
    val name = "Scala"
    val version = 2.12
    println(s"语言: $name, 版本: $version")           // 输出:语言: Scala, 版本: 2.12
    println(s"1 + 1 = ${1 + 1}")                     // 输出:1 + 1 = 2
    
    // f格式化插值:类似C语言的printf
    val pi = 3.14159
    println(f"圆周率: $pi%.2f")  // 输出:圆周率: 3.14(保留两位小数)
    
    // ==================== Unit和Null和Nothing ====================
    
    // Unit类型:表示"无值",类似Java的void,唯一实例是 ()
    val unitVal: Unit = ()
    println(s"Unit: $unitVal")    // 输出:Unit: ()
    
    // Null类型:null是所有引用类型(AnyRef)的子类的唯一实例
    val nullVal: Null = null
    println(s"Null: $nullVal")    // 输出:Null: null
    
    // ==================== 类型转换 ====================
    
    // 自动类型提升(隐式转换):小范围类型自动转为大范围类型
    val intNum: Int = 10
    val doubleNum: Double = intNum  // Int自动转为Double
    println(s"自动转换: $doubleNum") // 输出:自动转换: 10.0
    
    // 强制类型转换:大范围转小范围需要显式调用方法
    val d: Double = 9.99
    val i: Int = d.toInt            // Double转Int,截断小数部分
    println(s"强制转换: $i")         // 输出:强制转换: 9
    
    // 字符串转数值类型
    val str = "123"
    val num1: Int = str.toInt       // 字符串转Int
    val num2: Double = str.toDouble // 字符串转Double
    println(s"字符串转Int: $num1")    // 输出:字符串转Int: 123
    println(s"字符串转Double: $num2") // 输出:字符串转Double: 123.0
    
    // 数值类型转字符串
    val numStr: String = 100.toString
    println(s"数值转字符串: $numStr")  // 输出:数值转字符串: 100
  }
}

3.3 表达式

Scala中几乎所有语句都是表达式,表达式有返回值。

scala 复制代码
object ExpressionDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== if-else表达式 ====================
    
    // Scala中if-else是表达式,有返回值,返回值是最后一个表达式的值
    val score = 85
    
    // if-else表达式的返回值赋给变量grade
    val grade = if (score >= 90) {
      "优秀"    // 返回值
    } else if (score >= 80) {
      "良好"    // 返回值
    } else if (score >= 70) {
      "中等"    // 返回值
    } else if (score >= 60) {
      "及格"    // 返回值
    } else {
      "不及格"  // 返回值
    }
    println(s"成绩: $score, 等级: $grade")  // 输出:成绩: 85, 等级: 良好
    
    // 如果没有else分支,不满足条件时返回Unit类型(值为())
    val result = if (score >= 60) "及格"
    println(s"结果: $result")  // 输出:结果: 及格
    
    // ==================== 块表达式 ====================
    
    // 使用 {} 包裹的代码块是块表达式,块中最后一个表达式的值作为整个块的值
    val blockResult = {
      val a = 10      // 局部变量a
      val b = 20      // 局部变量b
      a + b           // 最后一个表达式,作为块表达式的返回值
    }
    println(s"块表达式结果: $blockResult")  // 输出:块表达式结果: 30
    
    // ==================== match表达式(模式匹配) ====================
    
    // 类似Java的switch,但更强大,使用 => 分隔模式和结果
    val dayOfWeek = 3
    
    val dayName = dayOfWeek match {
      case 1 => "星期一"    // 匹配值为1的情况
      case 2 => "星期二"    // 匹配值为2的情况
      case 3 => "星期三"    // 匹配值为3的情况
      case 4 => "星期四"    // 匹配值为4的情况
      case 5 => "星期五"    // 匹配值为5的情况
      case 6 => "星期六"    // 匹配值为6的情况
      case 7 => "星期日"    // 匹配值为7的情况
      case _ => "无效日期"  // _ 是通配符,匹配所有其他情况
    }
    println(s"今天是: $dayName")  // 输出:今天是: 星期三
    
    // 带条件守卫的模式匹配
    val x = 15
    val category = x match {
      case n if n > 0 && n <= 10  => "小数"   // 带if条件的模式
      case n if n > 10 && n <= 100 => "中数"
      case n if n > 100            => "大数"
      case _                        => "非正数"
    }
    println(s"$x 属于: $category")  // 输出:15 属于: 中数
    
    // 匹配类型
    val any: Any = 3.14
    val typeDesc = any match {
      case i: Int    => s"整数: $i"         // 匹配Int类型
      case d: Double => s"浮点数: $d"       // 匹配Double类型
      case s: String => s"字符串: $s"       // 匹配String类型
      case _         => "未知类型"           // 匹配其他所有类型
    }
    println(typeDesc)  // 输出:浮点数: 3.14
    
    // ==================== try-catch表达式 ====================
    
    // Scala的异常处理也是表达式,使用模式匹配来捕获异常
    val divideResult = try {
      val a = 10
      val b = 0
      a / b     // 会抛出 ArithmeticException
    } catch {
      case e: ArithmeticException => "除数不能为零"  // 捕获算术异常
      case e: Exception => s"发生异常: ${e.getMessage}" // 捕获其他异常
    } finally {
      // finally块:无论是否发生异常都会执行,通常用于清理资源
      println("finally块执行了")
    }
    println(s"除法结果: $divideResult")  // 输出:除法结果: 除数不能为零
  }
}

3.4 循环

scala 复制代码
object LoopDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== for循环 ====================
    
    // 基本for循环:使用 to 关键字(包含右边界)
    // to 表示 [1, 5],即1到5(包含5)
    println("=== to(包含右边界)===")
    for (i <- 1 to 5) {
      println(s"i = $i")  // 输出:1, 2, 3, 4, 5
    }
    
    // 使用 until 关键字(不包含右边界)
    // until 表示 [1, 5),即1到5(不包含5)
    println("=== until(不包含右边界)===")
    for (i <- 1 until 5) {
      println(s"i = $i")  // 输出:1, 2, 3, 4
    }
    
    // 指定步长(增量):使用 by 关键字
    println("=== 指定步长 ===")
    for (i <- 1 to 10 by 2) {
      println(s"i = $i")  // 输出:1, 3, 5, 7, 9(每次递增2)
    }
    
    // 倒序遍历:使用 reverse
    println("=== 倒序遍历 ===")
    for (i <- (1 to 5).reverse) {
      println(s"i = $i")  // 输出:5, 4, 3, 2, 1
    }
    
    // 循环遍历集合
    println("=== 遍历集合 ===")
    val fruits = List("苹果", "香蕉", "橘子")
    for (fruit <- fruits) {
      println(s"水果: $fruit")
    }
    
    // 嵌套循环:多个生成器用分号分隔
    println("=== 嵌套循环 ===")
    for (i <- 1 to 3; j <- 1 to 3) {
      println(s"i=$i, j=$j")
    }
    
    // for循环中的守卫条件(if过滤)
    println("=== 带条件的for循环 ===")
    // 只输出1到10中的偶数
    for (i <- 1 to 10 if i % 2 == 0) {
      println(s"偶数: $i")  // 输出:2, 4, 6, 8, 10
    }
    
    // for推导式(yield):将每次循环的结果收集到一个新集合中
    println("=== for推导式 ===")
    // 对1到5的每个数求平方,结果存入新集合
    val squares = for (i <- 1 to 5) yield i * i
    println(s"平方数: $squares")  // 输出:平方数: Vector(1, 4, 9, 16, 25)
    
    // for推导式 + 守卫条件
    val evenSquares = for (i <- 1 to 10 if i % 2 == 0) yield i * i
    println(s"偶数的平方: $evenSquares")  // 输出:偶数的平方: Vector(4, 16, 36, 64, 100)
    
    // ==================== while循环 ====================
    
    // while循环:先判断条件,再执行循环体
    println("=== while循环 ===")
    var count = 1               // 循环计数器,var可变变量
    while (count <= 5) {        // 条件:count小于等于5时继续循环
      println(s"count = $count") // 打印当前count值
      count += 1                // count自增1(Scala没有++运算符)
    }
    
    // ==================== do-while循环 ====================
    
    // do-while循环:先执行一次循环体,再判断条件
    println("=== do-while循环 ===")
    var num = 1                 // 循环计数器
    do {
      println(s"num = $num")   // 先执行循环体
      num += 1                 // num自增1
    } while (num <= 5)         // 再判断条件,条件为true则继续循环
    
    // ==================== 循环控制:使用breakable ====================
    
    // Scala没有break和continue关键字,需要导入scala.util.control.Breaks
    import scala.util.control.Breaks._  // 导入Breaks包中的所有成员
    
    // break:跳出循环
    println("=== break跳出循环 ===")
    breakable {                  // breakable块包裹需要break的循环
      for (i <- 1 to 10) {
        if (i == 6) {
          break()                // 当i等于6时,跳出循环
        }
        println(s"i = $i")      // 输出:1, 2, 3, 4, 5
      }
    }
    
    // continue的模拟:使用守卫条件或嵌套breakable
    println("=== 模拟continue ===")
    for (i <- 1 to 10) {
      breakable {               // 每次迭代都用breakable包裹
        if (i % 3 == 0) {
          break()                // 当i是3的倍数时,跳过本次(相当于continue)
        }
        println(s"i = $i")      // 输出:1, 2, 4, 5, 7, 8, 10
      }
    }
    
    // ==================== 九九乘法表案例 ====================
    
    println("=== 九九乘法表 ===")
    for (i <- 1 to 9) {                    // 外层循环:行,从1到9
      for (j <- 1 to i) {                  // 内层循环:列,从1到当前行号
        print(s"$j x $i = ${i * j}\t")     // 打印乘法表达式,\t为制表符对齐
      }
      println()                             // 每行打印完后换行
    }
    
    // 九九乘法表的简洁写法(使用分号分隔多个生成器)
    println("=== 九九乘法表(简洁写法)===")
    for (i <- 1 to 9; j <- 1 to i) {
      print(s"$j x $i = ${i * j}\t")
      if (j == i) println()  // 每行结束时换行
    }
  }
}

3.5 方法与函数

scala 复制代码
object MethodAndFunctionDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 方法的定义 ====================
    
    // 基本方法定义语法:
    // def 方法名(参数名: 参数类型, ...): 返回值类型 = { 方法体 }
    
    // 定义一个无参数、有返回值的方法
    def sayHello(): String = {
      "Hello, Scala!"     // 方法体最后一个表达式作为返回值
    }
    println(sayHello())    // 调用方法,输出:Hello, Scala!
    
    // 定义一个有参数、有返回值的方法
    // 返回值类型可以省略,Scala会自动推断
    def add(a: Int, b: Int): Int = {
      a + b               // 返回a + b的值
    }
    println(s"3 + 5 = ${add(3, 5)}")  // 输出:3 + 5 = 8
    
    // 省略返回值类型(自动推断)
    def multiply(a: Int, b: Int) = {
      a * b               // 编译器推断返回类型为Int
    }
    println(s"4 x 6 = ${multiply(4, 6)}")  // 输出:4 x 6 = 24
    
    // 无返回值的方法(返回Unit)
    def printInfo(name: String, age: Int): Unit = {
      println(s"姓名: $name, 年龄: $age")
    }
    printInfo("张三", 25)  // 输出:姓名: 张三, 年龄: 25
    
    // ==================== 方法参数的默认值 ====================
    
    // 参数可以设置默认值,调用时可以不传该参数
    def greet(name: String, greeting: String = "你好"): String = {
      s"$greeting, $name!"
    }
    println(greet("张三"))              // 使用默认值,输出:你好, 张三!
    println(greet("张三", "早上好"))    // 传入参数覆盖默认值,输出:早上好, 张三!
    
    // ==================== 带名参数 ====================
    
    // 调用方法时可以指定参数名,参数顺序可以不按定义顺序
    def createUser(name: String, age: Int, city: String): String = {
      s"$name, $age岁, 来自$city"
    }
    // 使用带名参数,顺序可以和定义不同
    println(createUser(city = "北京", name = "李四", age = 30))
    // 输出:李四, 30岁, 来自北京
    
    // ==================== 可变参数 ====================
    
    // 使用 * 表示可变参数(类似Java的 ...)
    def sum(numbers: Int*): Int = {
      var total = 0             // 初始化累加器
      for (n <- numbers) {      // 遍历可变参数
        total += n              // 逐个累加
      }
      total                     // 返回总和
    }
    println(s"求和: ${sum(1, 2, 3, 4, 5)}")    // 输出:求和: 15
    println(s"求和: ${sum(10, 20)}")            // 输出:求和: 30
    
    // 可变参数传入序列时需要使用 : _* 展开
    val nums = List(1, 2, 3, 4, 5)
    println(s"展开求和: ${sum(nums: _*)}")       // 输出:展开求和: 15
    
    // ==================== 函数 ====================
    
    // 函数是"一等公民",可以赋值给变量、作为参数传递
    
    // 定义函数并赋值给变量
    // 语法:val 函数变量名 = (参数列表) => { 函数体 }
    val addFunc = (a: Int, b: Int) => {
      a + b
    }
    println(s"函数调用: ${addFunc(3, 5)}")  // 输出:函数调用: 8
    
    // 指定函数类型
    val subFunc: (Int, Int) => Int = (a, b) => a - b
    println(s"减法: ${subFunc(10, 3)}")     // 输出:减法: 7
    
    // 无参数函数
    val getPI = () => 3.14159
    println(s"PI = ${getPI()}")             // 输出:PI = 3.14159
    
    // ==================== 方法转函数 ====================
    
    // 使用 _ 将方法转换为函数
    def subtract(a: Int, b: Int): Int = a - b   // 定义方法
    val subAsFunc = subtract _                    // 将方法转为函数
    println(s"方法转函数: ${subAsFunc(10, 3)}")   // 输出:方法转函数: 7
    
    // ==================== 高阶函数 ====================
    
    // 高阶函数:接收函数作为参数,或返回函数的函数
    
    // 1. 接收函数作为参数
    // operation参数是一个函数类型:(Int, Int) => Int
    def calculate(a: Int, b: Int, operation: (Int, Int) => Int): Int = {
      operation(a, b)    // 调用传入的函数
    }
    
    // 传入不同的函数实现不同的运算
    println(s"加法: ${calculate(10, 5, (a, b) => a + b)}")  // 输出:加法: 15
    println(s"乘法: ${calculate(10, 5, (a, b) => a * b)}")  // 输出:乘法: 50
    
    // 2. 返回函数的高阶函数
    def getOperation(op: String): (Int, Int) => Int = {
      op match {
        case "+" => (a, b) => a + b   // 返回加法函数
        case "-" => (a, b) => a - b   // 返回减法函数
        case "*" => (a, b) => a * b   // 返回乘法函数
        case _   => (a, b) => 0       // 默认返回零函数
      }
    }
    
    val addOp = getOperation("+")    // 获取加法函数
    println(s"高阶函数: ${addOp(8, 3)}")  // 输出:高阶函数: 11
    
    // ==================== 匿名函数 ====================
    
    // 匿名函数(Lambda表达式):没有函数名的函数
    
    // 基本匿名函数
    val double = (x: Int) => x * 2
    println(s"匿名函数: ${double(5)}")  // 输出:匿名函数: 10
    
    // 匿名函数作为参数直接传递
    val numbers = List(1, 2, 3, 4, 5)
    
    // map:对每个元素应用函数
    val doubled = numbers.map((x: Int) => x * 2)
    println(s"翻倍: $doubled")          // 输出:翻倍: List(2, 4, 6, 8, 10)
    
    // filter:过滤满足条件的元素
    val evens = numbers.filter((x: Int) => x % 2 == 0)
    println(s"偶数: $evens")            // 输出:偶数: List(2, 4)
    
    // 简写形式:当参数类型可以推断时,可以省略类型和括号
    val tripled = numbers.map(_ * 3)    // _ 代表每个元素
    println(s"三倍: $tripled")          // 输出:三倍: List(3, 6, 9, 12, 15)
    
    // ==================== 柯里化 ====================
    
    // 柯里化:将多参数函数转换为多个单参数函数的链式调用
    // 定义柯里化方法
    def curriedAdd(a: Int)(b: Int): Int = {
      a + b
    }
    // 调用柯里化方法
    println(s"柯里化: ${curriedAdd(3)(5)}")  // 输出:柯里化: 8
    
    // 部分应用函数:只传入部分参数,返回一个新的函数
    val add5 = curriedAdd(5)_   // 固定第一个参数为5
    println(s"部分应用: ${add5(3)}")  // 输出:部分应用: 8
    println(s"部分应用: ${add5(10)}") // 输出:部分应用: 15
  }
}

3.6 集合

3.6.1 数组(Array)
scala 复制代码
import scala.collection.mutable.ArrayBuffer

object ArrayDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 定长数组(Array)====================
    
    // 创建一个长度为5的Int数组,所有元素初始化为0
    val arr1 = new Array[Int](5)
    println(s"默认初始化: ${arr1.mkString(", ")}")
    // 输出:默认初始化: 0, 0, 0, 0, 0
    
    // 使用Array.apply直接创建并初始化数组
    val arr2 = Array(1, 2, 3, 4, 5)
    println(s"直接创建: ${arr2.mkString(", ")}")
    // 输出:直接创建: 1, 2, 3, 4, 5
    
    // 访问数组元素:使用 () 而不是 []
    println(s"第一个元素: ${arr2(0)}")  // 输出:第一个元素: 1
    println(s"第三个元素: ${arr2(2)}")  // 输出:第三个元素: 3
    
    // 修改数组元素
    arr2(0) = 10                        // 将第一个元素改为10
    println(s"修改后: ${arr2.mkString(", ")}")
    // 输出:修改后: 10, 2, 3, 4, 5
    
    // 数组长度
    println(s"数组长度: ${arr2.length}")  // 输出:数组长度: 5
    
    // 遍历数组 - 方式1:for循环
    println("方式1 - for循环:")
    for (i <- 0 until arr2.length) {
      print(s"${arr2(i)} ")             // 输出:10 2 3 4 5
    }
    println()
    
    // 遍历数组 - 方式2:增强for循环
    println("方式2 - 增强for:")
    for (elem <- arr2) {
      print(s"$elem ")                  // 输出:10 2 3 4 5
    }
    println()
    
    // 遍历数组 - 方式3:foreach
    println("方式3 - foreach:")
    arr2.foreach((elem: Int) => print(s"$elem "))  // 输出:10 2 3 4 5
    println()
    
    // 简写形式
    arr2.foreach(elem => print(s"$elem "))  // 参数类型可推断,省略类型
    println()
    
    // 使用 _ 简写
    arr2.foreach(print _)                   // 只有一个参数时可以用 _ 替代
    println()
    
    // ==================== 数组常用操作 ====================
    
    val nums = Array(3, 1, 4, 1, 5, 9, 2, 6)
    
    // 求和
    println(s"求和: ${nums.sum}")          // 输出:求和: 31
    
    // 求最大值
    println(s"最大值: ${nums.max}")        // 输出:最大值: 9
    
    // 求最小值
    println(s"最小值: ${nums.min}")        // 输出:最小值: 1
    
    // 排序(返回新数组,不修改原数组)
    val sorted = nums.sorted
    println(s"排序: ${sorted.mkString(", ")}")
    // 输出:排序: 1, 1, 2, 3, 4, 5, 6, 9
    
    // 降序排序
    val sortedDesc = nums.sortWith(_ > _)
    println(s"降序: ${sortedDesc.mkString(", ")}")
    // 输出:降序: 9, 6, 5, 4, 3, 2, 1, 1
    
    // 转换操作:map映射
    val doubled = nums.map(_ * 2)
    println(s"翻倍: ${doubled.mkString(", ")}")
    // 输出:翻倍: 6, 2, 8, 2, 10, 18, 4, 12
    
    // 过滤操作:filter
    val greaterThan3 = nums.filter(_ > 3)
    println(s"大于3: ${greaterThan3.mkString(", ")}")
    // 输出:大于3: 4, 5, 9, 6
    
    // ==================== 变长数组(ArrayBuffer)====================
    
    // 创建一个空的可变数组
    val buf = new ArrayBuffer[Int]()
    
    // 添加元素:+= 在末尾添加一个或多个元素
    buf += 1             // 添加元素1
    buf += 2             // 添加元素2
    buf += (3, 4, 5)    // 添加多个元素
    println(s"ArrayBuffer: $buf")   // 输出:ArrayBuffer: ArrayBuffer(1, 2, 3, 4, 5)
    
    // ++= 添加另一个集合的所有元素
    buf ++= Array(6, 7, 8)
    println(s"添加集合后: $buf")     // 输出:添加集合后: ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
    
    // 在指定位置插入元素
    buf.insert(0, 0)                 // 在索引0的位置插入元素0
    println(s"插入后: $buf")         // 输出:插入后: ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8)
    
    // 删除指定位置的元素
    buf.remove(0)                    // 删除索引0的元素
    println(s"删除后: $buf")         // 输出:删除后: ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
    
    // 删除指定范围的元素
    buf.remove(0, 3)                 // 从索引0开始删除3个元素
    println(s"范围删除后: $buf")     // 输出:范围删除后: ArrayBuffer(4, 5, 6, 7, 8)
    
    // 转换为定长数组
    val fixedArr = buf.toArray
    println(s"转为定长数组: ${fixedArr.mkString(", ")}")
    // 输出:转为定长数组: 4, 5, 6, 7, 8
    
    // ==================== 多维数组 ====================
    
    // 创建二维数组(3行4列)
    val matrix = Array.ofDim[Int](3, 4)
    
    // 给二维数组赋值
    for (i <- 0 until 3; j <- 0 until 4) {
      matrix(i)(j) = i * 4 + j   // 使用 () 访问二维数组元素
    }
    
    // 遍历二维数组
    println("二维数组:")
    for (i <- 0 until 3) {
      for (j <- 0 until 4) {
        print(f"${matrix(i)(j)}%4d")  // 使用f格式化,宽度为4
      }
      println()
    }
    // 输出:
    //    0   1   2   3
    //    4   5   6   7
    //    8   9  10  11
  }
}
3.6.2 List(列表)
scala 复制代码
import scala.collection.mutable.ListBuffer

object ListDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 不可变List(默认)====================
    
    // 创建不可变List
    // 使用 :: 操作符(cons操作符)创建列表
    val list1 = 1 :: 2 :: 3 :: 4 :: 5 :: Nil
    // Nil表示空列表,:: 是右结合的,从右向左构建
    println(s"列表: $list1")   // 输出:列表: List(1, 2, 3, 4, 5)
    
    // 使用List.apply创建
    val list2 = List(1, 2, 3, 4, 5)
    println(s"列表: $list2")   // 输出:列表: List(1, 2, 3, 4, 5)
    
    // 访问列表元素
    println(s"第一个元素: ${list2(0)}")   // 输出:第一个元素: 1
    println(s"第三个元素: ${list2(2)}")   // 输出:第三个元素: 3
    
    // 获取头部元素(第一个元素)
    println(s"头部: ${list2.head}")       // 输出:头部: 1
    
    // 获取尾部(除第一个以外的所有元素组成的列表)
    println(s"尾部: ${list2.tail}")       // 输出:尾部: List(2, 3, 4, 5)
    
    // 判断是否为空
    println(s"是否为空: ${list2.isEmpty}") // 输出:是否为空: false
    println(s"Nil是否为空: ${Nil.isEmpty}") // 输出:Nil是否为空: true
    
    // 列表长度
    println(s"长度: ${list2.length}")     // 输出:长度: 5
    
    // ==================== 不可变List的操作(返回新列表)====================
    
    // 在头部添加元素(:+ 和 +: 的区别)
    val list3 = 0 +: list2               // 在头部添加
    println(s"头部添加: $list3")          // 输出:头部添加: List(0, 1, 2, 3, 4, 5)
    
    val list4 = list2 :+ 6               // 在尾部添加
    println(s"尾部添加: $list4")          // 输出:尾部添加: List(1, 2, 3, 4, 5, 6)
    
    // 连接两个列表
    val list5 = List(1, 2, 3)
    val list6 = List(4, 5, 6)
    val list7 = list5 ++ list6            // ++ 连接两个列表
    println(s"连接: $list7")              // 输出:连接: List(1, 2, 3, 4, 5, 6)
    
    // map映射:对每个元素进行变换
    val doubled = list2.map(_ * 2)
    println(s"翻倍: $doubled")            // 输出:翻倍: List(2, 4, 6, 8, 10)
    
    // filter过滤
    val evens = list2.filter(_ % 2 == 0)
    println(s"偶数: $evens")              // 输出:偶数: List(2, 4)
    
    // flatMap:先map再flatten(展平)
    val nested = List(List(1, 2), List(3, 4), List(5))
    val flat = nested.flatMap(x => x)
    println(s"展平: $flat")               // 输出:展平: List(1, 2, 3, 4, 5)
    
    // foldLeft:从左向右折叠
    // 第一个参数是初始值,第二个参数是二元操作函数
    val sum = list2.foldLeft(0)(_ + _)
    println(s"求和(foldLeft): $sum")     // 输出:求和(foldLeft): 15
    
    // reduce:聚合操作(无初始值)
    val product = list2.reduce(_ * _)
    println(s"乘积(reduce): $product")   // 输出:乘积(reduce): 120
    
    // 分组:groupBy
    val nums = List(1, 2, 3, 4, 5, 6, 7, 8)
    val grouped = nums.groupBy(_ % 2)    // 按奇偶分组
    println(s"分组: $grouped")
    // 输出:分组: Map(0 -> List(2, 4, 6, 8), 1 -> List(1, 3, 5, 7))
    
    // take和drop
    println(s"前3个: ${list2.take(3)}")   // 输出:前3个: List(1, 2, 3)
    println(s"去掉前2个: ${list2.drop(2)}") // 输出:去掉前2个: List(3, 4, 5)
    
    // 去重
    val dupList = List(1, 2, 2, 3, 3, 3, 4)
    println(s"去重: ${dupList.distinct}")  // 输出:去重: List(1, 2, 3, 4)
    
    // 排序
    val unsorted = List(5, 3, 1, 4, 2)
    println(s"升序: ${unsorted.sorted}")           // 输出:升序: List(1, 2, 3, 4, 5)
    println(s"降序: ${unsorted.sortWith(_ > _)}")  // 输出:降序: List(5, 4, 3, 2, 1)
    
    // ==================== 可变List(ListBuffer)====================
    
    // 创建可变ListBuffer
    val buffer = ListBuffer(1, 2, 3)
    println(s"ListBuffer: $buffer")  // 输出:ListBuffer: ListBuffer(1, 2, 3)
    
    // 添加元素
    buffer += 4                       // 末尾添加单个元素
    buffer += (5, 6)                  // 末尾添加多个元素
    println(s"添加后: $buffer")       // 输出:添加后: ListBuffer(1, 2, 3, 4, 5, 6)
    
    // 添加另一个集合
    buffer ++= List(7, 8)
    println(s"合并后: $buffer")       // 输出:合并后: ListBuffer(1, 2, 3, 4, 5, 6, 7, 8)
    
    // 在指定位置插入
    buffer.insert(0, 0)               // 在索引0插入元素0
    println(s"插入后: $buffer")       // 输出:插入后: ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8)
    
    // 删除元素
    buffer -= 0                       // 删除值为0的元素
    println(s"删除后: $buffer")       // 输出:删除后: ListBuffer(1, 2, 3, 4, 5, 6, 7, 8)
    
    buffer.remove(0)                  // 删除索引0的元素
    println(s"按索引删除后: $buffer") // 输出:按索引删除后: ListBuffer(2, 3, 4, 5, 6, 7, 8)
    
    // 转换为不可变List
    val immutableList = buffer.toList
    println(s"转为不可变List: $immutableList")
    // 输出:转为不可变List: List(2, 3, 4, 5, 6, 7, 8)
  }
}
3.6.3 Map(映射)
scala 复制代码
import scala.collection.mutable

object MapDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 不可变Map ====================
    
    // 创建不可变Map
    val map1 = Map("name" -> "张三", "age" -> "25", "city" -> "北京")
    // -> 是创建键值对的操作符
    println(s"Map: $map1")
    // 输出:Map: Map(name -> 张三, age -> 25, city -> 北京)
    
    // 使用另一种语法创建Map
    val map2 = Map(("name", "李四"), ("age", "30"), ("city", "上海"))
    println(s"Map2: $map2")
    
    // 获取Map中的值:使用 ()
    println(s"姓名: ${map1("name")}")   // 输出:姓名: 张三
    
    // 使用get方法获取值,返回Option类型(Some或None)
    println(s"年龄: ${map1.get("age")}") // 输出:年龄: Some(25)
    
    // getOrElse:获取值,如果key不存在则返回默认值
    println(s"电话: ${map1.getOrElse("phone", "未填写")}")
    // 输出:电话: 未填写(因为"phone"键不存在)
    
    // 判断key是否存在
    println(s"是否存在name: ${map1.contains("name")}")    // 输出:是否存在name: true
    println(s"是否存在email: ${map1.contains("email")}")  // 输出:是否存在email: false
    
    // 遍历Map
    println("=== 遍历Map ===")
    
    // 方式1:遍历键值对
    for ((k, v) <- map1) {
      println(s"$k -> $v")
    }
    
    // 方式2:先遍历keys,再获取values
    for (key <- map1.keys) {
      println(s"$key -> ${map1(key)}")
    }
    
    // 方式3:foreach
    map1.foreach { case (k, v) => println(s"$k = $v") }
    
    // Map的转换操作
    // mapValues:对每个value进行变换
    val upperMap = map1.map { case (k, v) => (k, v.toString.toUpperCase) }
    println(s"转换后: $upperMap")
    
    // filter过滤
    val filtered = map1.filter { case (k, _) => k != "age" }
    println(s"过滤后: $filtered")
    // 输出:过滤后: Map(name -> 张三, city -> 北京)
    
    // ==================== 可变Map ====================
    
    // 创建可变Map
    val mutableMap = mutable.Map("name" -> "王五", "age" -> "28")
    println(s"可变Map: $mutableMap")
    
    // 添加或修改元素:使用 () = 语法
    mutableMap("city") = "广州"          // 添加新键值对
    mutableMap("name") = "赵六"          // 修改已有键的值
    println(s"更新后: $mutableMap")
    // 输出:更新后: Map(name -> 赵六, age -> 28, city -> 广州)
    
    // 使用 += 添加多个键值对
    mutableMap += ("email" -> "test@example.com", "phone" -> "13800138000")
    println(s"添加多个: $mutableMap")
    
    // 使用 -= 删除键值对
    mutableMap -= "phone"
    println(s"删除后: $mutableMap")
    
    // 使用put方法添加/修改
    mutableMap.put("country", "中国")
    println(s"put后: $mutableMap")
    
    // 使用remove删除
    mutableMap.remove("email")
    println(s"remove后: $mutableMap")
    
    // ==================== Map高级操作 ====================
    
    // 合并两个Map
    val mapA = Map("a" -> 1, "b" -> 2)
    val mapB = Map("b" -> 3, "c" -> 4)
    
    // ++ 合并(后面的覆盖前面的同名键)
    val merged = mapA ++ mapB
    println(s"合并: $merged")  // 输出:合并: Map(a -> 1, b -> 3, c -> 4)
    
    // WordCount案例:统计单词出现次数
    val words = Array("hello", "scala", "hello", "world", "scala", "hello")
    
    // 方法1:使用groupBy + mapValues
    val wordCount1 = words.groupBy(word => word).map { case (k, v) => (k, v.length) }
    println(s"词频统计1: $wordCount1")
    // 输出:词频统计1: Map(hello -> 3, scala -> 2, world -> 1)
    
    // 方法2:使用foldLeft
    val wordCount2 = words.foldLeft(Map[String, Int]()) { (acc, word) =>
      acc + (word -> (acc.getOrElse(word, 0) + 1))
    }
    println(s"词频统计2: $wordCount2")
    // 输出:词频统计2: Map(hello -> 3, scala -> 2, world -> 1)
  }
}
3.6.4 元组(Tuple)
scala 复制代码
object TupleDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 创建元组 ====================
    
    // 元组最多支持22个元素(Tuple1 ~ Tuple22)
    // 使用 () 创建元组,元素之间用逗号分隔
    
    // 创建二元组
    val t1 = (1, "hello")
    println(s"二元组: $t1")  // 输出:二元组: (1,hello)
    
    // 创建三元组
    val t2 = (1, "hello", 3.14)
    println(s"三元组: $t2")  // 输出:三元组: (1,hello,3.14)
    
    // 创建多元素元组
    val person = ("张三", 25, "北京", "工程师")
    println(s"人物: $person")
    
    // ==================== 访问元组元素 ====================
    
    // 使用 _1, _2, _3 ... 访问元组中的元素(从1开始计数)
    println(s"第一个元素: ${t2._1}")  // 输出:第一个元素: 1
    println(s"第二个元素: ${t2._2}")  // 输出:第二个元素: hello
    println(s"第三个元素: ${t2._3}")  // 输出:第三个元素: 3.14
    
    // 访问person元组的元素
    println(s"姓名: ${person._1}")    // 输出:姓名: 张三
    println(s"年龄: ${person._2}")    // 输出:年龄: 25
    println(s"城市: ${person._3}")    // 输出:城市: 北京
    
    // ==================== 元组解构 ====================
    
    // 使用模式匹配将元组解构为独立变量
    val (name, age, city, job) = person
    println(s"$name, $age岁, $city, $job")
    // 输出:张三, 25岁, 北京, 工程师
    
    // 二元组解构
    val (id, value) = (1001, "Scala")
    println(s"id=$id, value=$value")  // 输出:id=1001, value=Scala
    
    // ==================== 元组遍历 ====================
    
    // 使用productIterator遍历元组
    val t3 = (1, 2, 3, 4, 5)
    print("遍历元组: ")
    t3.productIterator.foreach(i => print(s"$i "))
    // 输出:遍历元组: 1 2 3 4 5
    println()
    
    // ==================== 元组转字符串 ====================
    
    val t4 = ("hello", 42, true)
    println(s"toString: ${t4.toString}")  // 输出:toString: (hello,42,true)
    
    // 使用toString方法的简写
    println(s"拼接: ${t4.toString(", ")}") // 输出:拼接: hello,42,true
    
    // ==================== 元组在函数中的应用 ====================
    
    // 函数返回多个值时使用元组
    def getMinMax(arr: Array[Int]): (Int, Int) = {
      (arr.min, arr.max)  // 返回一个二元组
    }
    
    val nums = Array(3, 1, 4, 1, 5, 9, 2, 6)
    val (min, max) = getMinMax(nums)  // 解构接收返回值
    println(s"最小值: $min, 最大值: $max")
    // 输出:最小值: 1, 最大值: 9
    
    // 元组用于swap交换
    val swapPair = (1, "two").swap
    println(s"交换后: $swapPair")  // 输出:交换后:(two,1)
    
    // ==================== 元组与zip ====================
    
    // zip:将两个集合配对成元组的集合
    val keys = List("name", "age", "city")
    val values = List("张三", 25, "北京")
    val zipped = keys.zip(values)
    println(s"zip结果: $zipped")
    // 输出:zip结果: List((name,张三), (age,25), (city,北京))
    
    // zip后的结果可以直接转为Map
    val zippedMap = zipped.toMap
    println(s"转Map: $zippedMap")
    // 输出:转Map: Map(name -> 张三, age -> 25, city -> 北京)
    
    // unzip:将元组集合拆分为两个集合
    val (unzipKeys, unzipValues) = zipped.unzip
    println(s"unzip: keys=$unzipKeys, values=$unzipValues")
    // 输出:unzip: keys=List(name, age, city), values=List(张三, 25, 北京)
  }
}
3.6.5 Set(集合)
scala 复制代码
import scala.collection.mutable

object SetDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 不可变Set ====================
    
    // 创建不可变Set(元素自动去重)
    val set1 = Set(1, 2, 3, 4, 5, 3, 2, 1)
    println(s"Set: $set1")     // 输出:Set: Set(5, 1, 2, 3, 4)(无序且去重)
    
    // Set的特点:无序、不重复
    // 判断元素是否包含在Set中
    println(s"包含3: ${set1.contains(3)}")    // 输出:包含3: true
    println(s"包含6: ${set1.contains(6)}")    // 输出:包含6: false
    
    // 添加元素(返回新Set,原Set不变)
    val set2 = set1 + 6
    println(s"添加6后: $set2")   // 输出:添加6后: Set(5, 1, 6, 2, 3, 4)
    println(s"原Set: $set1")     // 输出:原Set: Set(5, 1, 2, 3, 4)(未改变)
    
    // 删除元素(返回新Set)
    val set3 = set1 - 3
    println(s"删除3后: $set3")   // 输出:删除3后: Set(5, 1, 2, 4)
    
    // ==================== 集合运算 ====================
    
    val setA = Set(1, 2, 3, 4, 5)
    val setB = Set(4, 5, 6, 7, 8)
    
    // 并集(union):包含两个集合的所有元素
    val unionSet = setA.union(setB)
    // 或者使用 ++ 操作符
    val unionSet2 = setA ++ setB
    println(s"并集: $unionSet")     // 输出:并集: Set(5, 1, 6, 2, 7, 3, 8, 4)
    
    // 交集(intersect):只包含两个集合共有的元素
    val intersectSet = setA.intersect(setB)
    // 或者使用 & 操作符
    val intersectSet2 = setA & setB
    println(s"交集: $intersectSet") // 输出:交集: Set(5, 4)
    
    // 差集(diff):在setA中但不在setB中的元素
    val diffSet = setA.diff(setB)
    // 或者使用 &~ 操作符
    val diffSet2 = setA &~ setB
    println(s"差集: $diffSet")      // 输出:差集: Set(1, 2, 3)
    
    // 子集判断
    val setC = Set(1, 2, 3)
    println(s"setC是setA的子集: ${setC.subsetOf(setA)}")
    // 输出:setC是setA的子集: true
    
    // 遍历Set
    println("遍历Set:")
    for (elem <- set1) {
      print(s"$elem ")
    }
    println()
    
    // foreach遍历
    set1.foreach(elem => print(s"$elem "))
    println()
    
    // ==================== 可变Set ====================
    
    // 创建可变Set
    val mSet = mutable.Set(1, 2, 3)
    println(s"可变Set: $mSet")
    
    // 添加元素(直接修改原Set)
    mSet += 4
    mSet += (5, 6)
    println(s"添加后: $mSet")       // 输出:添加后: Set(1, 2, 3, 4, 5, 6)
    
    // 使用add方法添加
    mSet.add(7)
    println(s"add后: $mSet")        // 输出:add后: Set(1, 2, 3, 4, 5, 6, 7)
    
    // 删除元素
    mSet -= 3
    println(s"删除后: $mSet")       // 输出:删除后: Set(1, 2, 4, 5, 6, 7)
    
    mSet.remove(4)
    println(s"remove后: $mSet")     // 输出:remove后: Set(1, 2, 5, 6, 7)
    
    // 添加另一个集合的所有元素
    mSet ++= Set(8, 9, 10)
    println(s"合并后: $mSet")       // 输出:合并后: Set(1, 2, 5, 6, 7, 8, 9, 10)
    
    // 删除另一个集合的所有元素
    mSet --= Set(1, 2)
    println(s"减去后: $mSet")       // 输出:减去后: Set(5, 6, 7, 8, 9, 10)
    
    // ==================== Set的常用方法 ====================
    
    val numbers = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    // 大小
    println(s"大小: ${numbers.size}")          // 输出:大小: 10
    
    // 转换操作
    val mapped = numbers.map(_ * 2)
    println(s"翻倍: $mapped")                  // 输出:翻倍: Set(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
    
    // 过滤
    val evens = numbers.filter(_ % 2 == 0)
    println(s"偶数: $evens")                   // 输出:偶数: Set(2, 4, 6, 8, 10)
    
    // 归约
    val sum = numbers.reduce(_ + _)
    println(s"总和: $sum")                     // 输出:总和: 55
    
    // 转换为其他集合
    val toList = numbers.toList
    println(s"转List: $toList")
    
    val toArray = numbers.toArray
    println(s"转Array: ${toArray.mkString(", ")}")
  }
}

四、类和对象

4.1 类的定义

scala 复制代码
// ==================== 基本类的定义 ====================

// 定义一个Person类
// 在Scala中,class关键字定义类,类中的成员默认是public
class Person {
  // 使用var定义可变属性(会自动生成getter和setter)
  var name: String = _      // _ 表示使用默认值(String默认为null)
  var age: Int = _           // _ 表示使用默认值(Int默认为0)
  
  // 使用val定义不可变属性(只生成getter,不生成setter)
  val birthYear: Int = 2000
  
  // 定义方法
  def introduce(): String = {
    s"我叫$name,今年$age岁,出生于$birthYear年"
  }
  
  // 定义带参数的方法
  def isAdult(): Boolean = {
    age >= 18
  }
}

object ClassDemo1 {
  def main(args: Array[String]): Unit = {
    
    // 创建对象:使用new关键字
    val p1 = new Person()
    
    // 设置属性值(使用自动生成的setter)
    p1.name = "张三"
    p1.age = 25
    
    // 获取属性值(使用自动生成的getter)
    println(s"姓名: ${p1.name}")     // 输出:姓名: 张三
    println(s"年龄: ${p1.age}")      // 输出:年龄: 25
    
    // 调用方法
    println(p1.introduce())
    // 输出:我叫张三,今年25岁,出生于2000年
    println(s"是否成年: ${p1.isAdult()}")
    // 输出:是否成年: true
    
    // 注意:val属性不能修改
    // p1.birthYear = 2001  // 错误:Reassignment to val
  }
}

// ==================== 带构造参数的类 ====================

// 主构造器参数直接写在类名后面
// 不加 val/var 表示构造器参数(不是类的字段,外部无法访问)
// 加 val 表示不可变属性(只读)
// 加 var 表示可变属性(可读可写)
class Student(val name: String, var age: Int, gender: String) {
  // gender没有val/var修饰,只是构造器参数,不是类的字段
  // 但在类内部可以使用gender
  
  def info(): String = {
    s"姓名: $name, 年龄: $age, 性别: $gender"
  }
}

object ClassDemo2 {
  def main(args: Array[String]): Unit = {
    
    // 创建Student对象时传入构造参数
    val s1 = new Student("李四", 20, "男")
    
    // name是val,可以读取但不能修改
    println(s"姓名: ${s1.name}")     // 输出:姓名: 李四
    // s1.name = "王五"              // 错误:val不能修改
    
    // age是var,可以读取和修改
    println(s"年龄: ${s1.age}")      // 输出:年龄: 20
    s1.age = 21                       // 正确:var可以修改
    println(s"修改后年龄: ${s1.age}") // 输出:修改后年龄: 21
    
    // gender不是字段,外部无法访问
    // println(s1.gender)            // 错误:无法访问gender
    
    println(s1.info())
    // 输出:姓名: 李四, 年龄: 21, 性别: 男
  }
}

4.2 单例对象

scala 复制代码
// ==================== 单例对象(object)====================

// 使用object关键字定义单例对象
// 单例对象在第一次被访问时初始化,全局只有一个实例
// 单例对象的名称不能和类名相同(否则就是伴生对象了)

// 工具类示例:使用单例对象实现工具方法
object MathUtils {
  // 定义常量
  val PI: Double = 3.141592653589793
  
  // 定义工具方法
  def abs(x: Int): Int = {
    if (x >= 0) x else -x
  }
  
  def max(a: Int, b: Int): Int = {
    if (a > b) a else b
  }
  
  def min(a: Int, b: Int): Int = {
    if (a < b) a else b
  }
  
  def circleArea(radius: Double): Double = {
    PI * radius * radius
  }
}

object SingletonDemo {
  def main(args: Array[String]): Unit = {
    
    // 直接通过单例对象名调用,不需要new
    println(s"PI = ${MathUtils.PI}")
    // 输出:PI = 3.141592653589793
    
    println(s"abs(-5) = ${MathUtils.abs(-5)}")
    // 输出:abs(-5) = 5
    
    println(s"max(3, 7) = ${MathUtils.max(3, 7)}")
    // 输出:max(3, 7) = 7
    
    println(s"圆面积(r=5) = ${MathUtils.circleArea(5)}")
    // 输出:圆面积(r=5) = 78.53981633974483
  }
}

// ==================== 伴生类和伴生对象中的单例对象 ====================

// 当一个object的名字和一个class的名字相同时
// 这个object叫做这个class的伴生对象(Companion Object)
// 这个class叫做这个object的伴生类(Companion Class)
// 伴生类和伴生对象可以互相访问对方的私有成员

// 单例对象也可以有apply方法,使其可以像函数一样被调用
object StringUtils {
  // apply方法:使对象可以像函数一样调用
  def apply(s: String): StringUtils = {
    new StringUtils(s)
  }
}

class StringUtils(val value: String) {
  def reverse: String = value.reverse
  def length: Int = value.length
}

object StringUtilsDemo {
  def main(args: Array[String]): Unit = {
    // 使用new创建实例(常规方式)
    val s1 = new StringUtils("hello")
    println(s1.reverse)  // 输出:olleh
    
    // 使用伴生对象的apply方法创建实例(简洁方式)
    val s2 = StringUtils("world")  // 等价于 StringUtils.apply("world")
    println(s2.reverse)  // 输出:dlrow
  }
}

4.3 伴生对象

scala 复制代码
// ==================== 伴生对象详解 ====================

// 伴生类:包含实例成员
// 伴生对象:包含静态成员(Scala中没有static关键字,用伴生对象实现)
// 两者必须在同一个源文件中

// 伴生类
class Account(val id: Int, val name: String) {
  private var balance: Double = 0.0  // 私有属性
  
  // 存款
  def deposit(amount: Double): Unit = {
    if (amount > 0) {
      balance += amount
      println(s"$name 存入 $amount,余额: $balance")
    }
  }
  
  // 取款
  def withdraw(amount: Double): Unit = {
    if (amount > 0 && amount <= balance) {
      balance -= amount
      println(s"$name 取出 $amount,余额: $balance")
    } else {
      println("余额不足")
    }
  }
  
  // 获取余额(提供只读访问)
  def getBalance: Double = balance
  
  override def toString: String = s"Account($id, $name, balance=$balance)"
}

// 伴生对象:包含与类相关的"静态"成员
object Account {
  private var idCounter: Int = 0  // 私有计数器,伴生类也能访问
  
  // 工厂方法:创建Account实例
  def apply(name: String, initialBalance: Double = 0): Account = {
    idCounter += 1              // 递增ID
    val account = new Account(idCounter, name)  // 创建实例
    if (initialBalance > 0) {
      account.deposit(initialBalance)  // 存入初始余额
    }
    account                     // 返回创建的实例
  }
  
  // 静态工具方法
  def getTotalCreated: Int = idCounter
}

object CompanionDemo {
  def main(args: Array[String]): Unit = {
    
    // 使用伴生对象的apply工厂方法创建账户
    val acc1 = Account("张三", 1000)   // 创建账户,初始余额1000
    val acc2 = Account("李四")         // 创建账户,初始余额为0
    
    println(acc1)  // 输出:Account(1, 张三, balance=1000.0)
    println(acc2)  // 输出:Account(2, 李四, balance=0.0)
    
    // 进行操作
    acc1.deposit(500)     // 存款500
    acc1.withdraw(200)    // 取款200
    println(s"余额: ${acc1.getBalance}")
    // 输出:余额: 1300.0
    
    // 通过伴生对象获取已创建的账户总数
    println(s"已创建账户数: ${Account.getTotalCreated}")
    // 输出:已创建账户数: 2
  }
}

4.4 get和set方法

scala 复制代码
// ==================== Scala中属性的getter和setter ====================

// Scala中,var属性自动生成getter和setter
// val属性只生成getter
// 也可以自定义getter和setter

class Employee {
  // var属性:自动生成name(getter)和 name_(setter)
  var name: String = _
  
  // val属性:只生成getter
  val id: Int = 1001
  
  // 使用private var + 自定义getter/setter实现数据验证
  private var _age: Int = 0        // 私有字段,以下划线开头命名
  
  // 自定义getter方法:字段名 不带下划线
  def age: Int = _age
  
  // 自定义setter方法:字段名_= (注意空格和等号)
  def age_=(newAge: Int): Unit = {
    if (newAge >= 0 && newAge <= 150) {  // 验证年龄范围
      _age = newAge                       // 合法则赋值
    } else {
      println(s"无效的年龄: $newAge")     // 不合法则提示
    }
  }
  
  // 使用BeanProperty注解自动生成Java风格的getter/setter
  // 需要导入:import scala.beans.BeanProperty
  
  override def toString: String = s"Employee($id, $name, age=$_age)"
}

object GetterSetterDemo {
  def main(args: Array[String]): Unit = {
    
    val emp = new Employee()
    
    // 使用自动生成的setter设置name
    emp.name = "张三"
    // 使用自动生成的getter获取name
    println(s"姓名: ${emp.name}")     // 输出:姓名: 张三
    
    // val属性只有getter
    println(s"ID: ${emp.id}")         // 输出:ID: 1001
    // emp.id = 1002                  // 错误:val不能修改
    
    // 使用自定义的setter设置age(会进行验证)
    emp.age = 25                      // 合法年龄
    println(s"年龄: ${emp.age}")      // 输出:年龄: 25
    
    emp.age = -5                      // 无效年龄
    // 输出:无效的年龄: -5
    
    emp.age = 200                     // 无效年龄
    // 输出:无效的年龄: 200
    
    println(s"最终: $emp")
    // 输出:最终:Employee(1001, 张三, age=25)
  }
}

// ==================== @BeanProperty注解 ====================

import scala.beans.BeanProperty

class Product {
  // @BeanProperty注解:同时生成Scala风格和Java风格的getter/setter
  // Scala风格:name / name_=
  // Java风格:getName() / setName()
  @BeanProperty var productName: String = _
  @BeanProperty var price: Double = _
}

object BeanPropertyDemo {
  def main(args: Array[String]): Unit = {
    val p = new Product()
    
    // Scala风格的setter/getter
    p.productName = "笔记本电脑"
    println(s"产品名: ${p.productName}")
    
    // Java风格的setter/getter(由@BeanProperty生成)
    p.setPrice(5999.0)
    println(s"价格: ${p.getPrice}")
    
    println(s"${p.getProductName}, 价格: ${p.getPrice}")
    // 输出:笔记本电脑, 价格: 5999.0
  }
}

4.5 构造器

scala 复制代码
// ==================== Scala构造器 ====================

// Scala中构造器分为主构造器和辅助构造器

// ---- 主构造器 ----
// 主构造器的参数直接写在类名后的圆括号中
// 主构造器的参数可以有默认值
// 主构造器的代码就是类体中所有不在方法中的代码

class Animal(val name: String, val species: String, var age: Int = 0) {
  // 以下代码属于主构造器的执行体(在创建对象时执行)
  println(s"创建Animal对象: $name, $species, $age岁")
  
  def info(): String = {
    s"$name 是一只 $species,$age 岁"
  }
}

// ---- 辅助构造器 ----
// 辅助构造器使用 def this(...) 定义
// 辅助构造器的第一行必须调用主构造器或其他辅助构造器

class Car(val brand: String, val model: String, val year: Int) {
  var color: String = "白色"         // 默认颜色
  var price: Double = 0.0            // 默认价格
  
  // 辅助构造器1:只有品牌和型号
  def this(brand: String, model: String) = {
    this(brand, model, 2024)         // 调用主构造器,默认年份2024
    println("调用辅助构造器1")
  }
  
  // 辅助构造器2:只有品牌
  def this(brand: String) = {
    this(brand, "未知型号")          // 调用辅助构造器1
    println("调用辅助构造器2")
  }
  
  // 带颜色和价格的辅助构造器
  def this(brand: String, model: String, year: Int, color: String, price: Double) = {
    this(brand, model, year)         // 调用主构造器
    this.color = color               // 设置颜色
    this.price = price               // 设置价格
    println("调用带颜色和价格的辅助构造器")
  }
  
  override def toString: String = {
    s"Car($brand, $model, $year, $color, ¥$price)"
  }
}

// ---- 私有构造器 ----
// 构造器参数前加private,表示不能在外部直接new
class Database private(val url: String, val username: String) {
  def connect(): Unit = {
    println(s"连接数据库: $url, 用户: $username")
  }
}

object Database {
  // 通过伴生对象的工厂方法来创建实例
  def apply(url: String, username: String): Database = {
    new Database(url, username)
  }
}

object ConstructorDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 使用主构造器 ====================
    println("=== 主构造器 ===")
    val dog = new Animal("旺财", "狗", 3)
    // 输出:创建Animal对象: 旺财, 狗, 3岁
    println(dog.info())
    // 输出:旺财 是一只 狗,3 岁
    
    // 使用默认参数值
    val cat = new Animal("咪咪", "猫")
    // 输出:创建Animal对象: 咪咪, 猫, 0岁
    println(cat.info())
    // 输出:咪咪 是一只 猫,0 岁
    
    // ==================== 使用辅助构造器 ====================
    println("\n=== 辅助构造器 ===")
    
    // 调用主构造器(3个参数)
    val car1 = new Car("宝马", "X5", 2024)
    println(car1)
    // 输出:Car(宝马, X5, 2024, 白色, ¥0.0)
    
    // 调用辅助构造器1(2个参数)
    val car2 = new Car("奔驰", "C200")
    println(car2)
    // 输出:调用辅助构造器1
    // Car(奔驰, C200, 2024, 白色, ¥0.0)
    
    // 调用辅助构造器2(1个参数)
    val car3 = new Car("丰田")
    println(car3)
    // 输出:调用辅助构造器1
    // 调用辅助构造器2
    // Car(丰田, 未知型号, 2024, 白色, ¥0.0)
    
    // 调用带颜色和价格的辅助构造器
    val car4 = new Car("特斯拉", "Model 3", 2024, "黑色", 249900)
    println(car4)
    // 输出:调用带颜色和价格的辅助构造器
    // Car(特斯拉, Model 3, 2024, 黑色, ¥249900.0)
    
    // ==================== 私有构造器 ====================
    println("\n=== 私有构造器 ===")
    // val db = new Database("jdbc:mysql://localhost", "root")  // 错误:构造器是私有的
    val db = Database("jdbc:mysql://localhost:3306/mydb", "root")  // 通过伴生对象创建
    db.connect()
    // 输出:连接数据库: jdbc:mysql://localhost:3306/mydb, 用户: root
    
    // ==================== 构造器参数 ====================
    println("\n=== 构造器参数说明 ===")
    
    // 不带val/var的参数:仅在类内部可访问,不是字段
    class Demo1(x: Int) {
      def showX(): Int = x  // 类内部可以访问
    }
    val d1 = new Demo1(10)
    // d1.x  // 错误:外部无法访问
    
    // val参数:不可变字段,外部只读
    class Demo2(val x: Int)
    val d2 = new Demo2(20)
    println(s"val参数: ${d2.x}")   // 可以读取
    // d2.x = 30                   // 不能修改
    
    // var参数:可变字段,外部可读可写
    class Demo3(var x: Int)
    val d3 = new Demo3(40)
    println(s"var参数: ${d3.x}")   // 可以读取
    d3.x = 50                       // 可以修改
    println(s"修改后: ${d3.x}")    // 输出:修改后: 50
    
    // private val参数:私有字段,只有类内部可访问
    class Demo4(private val x: Int) {
      def getX: Int = x
    }
    val d4 = new Demo4(60)
    println(s"private val: ${d4.getX}") // 通过方法访问
  }
}

4.6 抽象类和特质

4.6.1 抽象类
scala 复制代码
// ==================== 抽象类 ====================

// 使用abstract关键字定义抽象类
// 抽象类中可以包含:
// 1. 抽象方法:只有方法声明,没有方法体
// 2. 抽象字段:只有声明,没有初始化
// 3. 具体方法:有完整实现的方法
// 4. 具体字段:有初始值的字段

// 抽象类不能被直接实例化(不能new)
// 抽象类需要被子类继承并实现所有抽象成员

abstract class Shape {
  // 抽象字段:没有初始值
  val color: String
  
  // 抽象方法:没有方法体
  def area(): Double
  def perimeter(): Double
  
  // 具体方法:有实现
  def describe(): String = {
    s"这是一个${color}的图形,面积=${f"${area()}%.2f"},周长=${f"${perimeter()}%.2f"}"
  }
}

// 继承抽象类,使用extends关键字
// 子类必须实现所有抽象成员
class Circle(val color: String, val radius: Double) extends Shape {
  
  // 实现抽象方法:计算圆的面积
  override def area(): Double = {
    Math.PI * radius * radius    // 面积 = π × r²
  }
  
  // 实现抽象方法:计算圆的周长
  override def perimeter(): Double = {
    2 * Math.PI * radius         // 周长 = 2 × π × r
  }
  
  // override关键字可以省略(建议保留,增加可读性)
  override def describe(): String = {
    s"这是一个${color}的圆形,半径=$radius,面积=${f"${area()}%.2f"}"
  }
}

// 矩形类
class Rectangle(val color: String, val width: Double, val height: Double) extends Shape {
  
  override def area(): Double = width * height         // 面积 = 宽 × 高
  override def perimeter(): Double = 2 * (width + height) // 周长 = 2 ×(宽 + 高)
}

// 三角形类
class Triangle(val color: String, val a: Double, val b: Double, val c: Double) extends Shape {
  
  override def area(): Double = {
    // 海伦公式:面积 = √(s × (s-a) × (s-b) × (s-c))
    val s = (a + b + c) / 2     // 半周长
    Math.sqrt(s * (s - a) * (s - b) * (s - c))
  }
  
  override def perimeter(): Double = a + b + c         // 周长 = 三边之和
}

object AbstractClassDemo {
  def main(args: Array[String]): Unit = {
    
    // 不能直接创建抽象类的实例
    // val s = new Shape()  // 错误:不能实例化抽象类
    
    // 创建子类实例
    val circle = new Circle("红色", 5.0)
    println(circle.describe())
    // 输出:这是一个红色的圆形,半径=5.0,面积=78.54
    
    val rect = new Rectangle("蓝色", 4.0, 6.0)
    println(rect.describe())
    // 输出:这是一个蓝色的图形,面积=24.00,周长=20.00
    
    val triangle = new Triangle("绿色", 3.0, 4.0, 5.0)
    println(triangle.describe())
    // 输出:这是一个绿色的图形,面积=6.00,周长=12.00
    
    // 多态:使用父类引用指向子类对象
    val shapes: List[Shape] = List(circle, rect, triangle)
    
    // 遍历所有图形,调用统一接口
    for (shape <- shapes) {
      println(shape.describe())
    }
    
    // 计算所有图形的总面积
    val totalArea = shapes.map(_.area()).sum
    println(s"总面积: ${f"$totalArea%.2f"}")
    // 输出:总面积: 108.54
  }
}
4.6.2 特质(Trait)
scala 复制代码
// ==================== 特质(Trait)====================

// 特质是Scala中实现多重继承的方式(类似Java的接口,但更强大)
// 特质中可以包含:
// 1. 抽象方法和字段
// 2. 具体方法和字段
// 3. 可以被多个类混入(mixin)

// ---- 定义特质 ----

// 特质1:可打印
trait Printable {
  // 抽象方法
  def printContent(): String
  
  // 具体方法
  def printWithBorder(): Unit = {
    val content = printContent()        // 调用抽象方法
    val border = "=" * (content.length + 4)
    println(border)
    println(s"| $content |")
    println(border)
  }
}

// 特质2:可序列化
trait Serializable {
  // 具体字段
  var serialized: Boolean = false
  
  // 具体方法
  def serialize(): String = {
    serialized = true
    s"[序列化完成: ${this.getClass.getSimpleName}]"
  }
  
  def deserialize(): String = {
    serialized = false
    s"[反序列化完成: ${this.getClass.getSimpleName}]"
  }
}

// 特质3:可记录日志
trait Logger {
  // 定义日志级别方法
  def log(message: String): Unit = {
    println(s"[${this.getClass.getSimpleName} - LOG] $message")
  }
  
  def warn(message: String): Unit = {
    println(s"[${this.getClass.getSimpleName} - WARN] $message")
  }
  
  def error(message: String): Unit = {
    println(s"[${this.getClass.getSimpleName} - ERROR] $message")
  }
}

// 特质4:带抽象字段的特质
trait HasId {
  val id: Int                    // 抽象字段
  def getIdInfo: String = s"ID: $id"
}

// ---- 混入特质 ----

// 使用 with 关键字混入多个特质
// 使用 extends 继承第一个特质
class Document(val id: Int, val title: String, val content: String) 
  extends HasId with Printable with Serializable with Logger {
  
  // 实现Printable特质的抽象方法
  override def printContent(): String = {
    s"[$title] $content"
  }
  
  // 重写特质的具体方法
  override def log(message: String): Unit = {
    println(s"[Document-$title - LOG] $message")
  }
}

// 另一个混入特质的类
class User(val id: Int, val name: String, val email: String) 
  extends HasId with Logger with Serializable {
  
  def register(): Unit = {
    log(s"用户 $name 已注册")          // 使用Logger特质的方法
    println(serialize())                 // 使用Serializable特质的方法
    println(getIdInfo)                   // 使用HasId特质的方法
  }
}

// ---- 特质的动态混入 ----

// 可以在创建对象时动态混入特质
class BasicService(val id: Int) extends HasId {
  def serve(): String = s"基础服务 [ID: $id]"
}

object TraitDemo {
  def main(args: Array[String]): Unit = {
    
    // ==================== 基本使用 ====================
    println("=== Document示例 ===")
    val doc = new Document(1, "Scala入门", "学习Scala语言")
    
    // 使用Logger特质的方法(被重写了)
    doc.log("文档已创建")
    // 输出:[Document-Scala入门 - LOG] 文档已创建
    
    // 使用Printable特质的方法
    doc.printContent()        // 输出:[Scala入门] 学习Scala语言
    doc.printWithBorder()     // 输出带边框的文本
    // ====================
    // | [Scala入门] 学习Scala语言 |
    // ====================
    
    // 使用Serializable特质的方法
    println(doc.serialize())      // 输出:[序列化完成: Document]
    println(s"是否已序列化: ${doc.serialized}")  // 输出:是否已序列化: true
    
    println()
    
    // ==================== User示例 ====================
    println("=== User示例 ===")
    val user = new User(1001, "张三", "zhangsan@example.com")
    user.register()
    // 输出:[User - LOG] 用户 张三 已注册
    // 输出:[序列化完成: User]
    // 输出:ID: 1001
    
    println()
    
    // ==================== 动态混入 ====================
    println("=== 动态混入 ===")
    
    // 在创建对象时动态混入Logger特质
    val service = new BasicService(2001) with Logger
    service.log("服务已启动")
    // 输出:[BasicService - LOG] 服务已启动
    println(service.serve())
    // 输出:基础服务 [ID: 2001]
    
    // 同时混入多个特质
    val service2 = new BasicService(2002) with Logger with Serializable
    service2.log("带序列化的服务已启动")
    println(service2.serialize())
    
    println()
    
    // ==================== 特质线性化 ====================
    println("=== 特质线性化 ===")
    // 当多个特质有相同方法时,混入的顺序决定调用顺序
    // 最右边的特质最先被调用
    
    trait A {
      def show(): Unit = println("A")
    }
    
    trait B extends A {
      override def show(): Unit = println("B")
    }
    
    trait C extends A {
      override def show(): Unit = println("C")
    }
    
    // 从右到左的线性化:D -> C -> B -> A
    class D extends B with C {
      // C在最右边,所以C的show()被调用
    }
    
    val d = new D()
    d.show()  // 输出:C
    
    // 使用super调用父特质的方法
    class E extends B with C {
      override def show(): Unit = {
        super.show()    // 调用C的show()
        println("E")
      }
    }
    
    val e = new E()
    e.show()
    // 输出:
    // C
    // E
  }
}

五、使用Eclipse创建Scala项目

5.1 安装Scala for Eclipse IDE

步骤一:下载Scala IDE

  1. 访问Scala IDE官网:http://scala-ide.org/
  2. 下载 Scala IDE for Eclipse 安装包(包含Scala插件的Eclipse集成版本)
  3. 或者使用已有的Eclipse,通过Marketplace安装Scala插件

步骤二:Eclipse Marketplace安装(推荐)

  1. 打开Eclipse IDE
  2. 点击菜单栏 HelpEclipse Marketplace...
  3. 在搜索框中输入 "scala"
  4. 找到 "Scala IDE" 插件,点击 Install
  5. 按照提示完成安装,确认所有依赖项
  6. 点击 Finish,等待安装完成
  7. 重启Eclipse

步骤三:验证安装

  1. 重启Eclipse后,点击 HelpAbout Eclipse
  2. 检查是否有Scala IDE的图标或信息
  3. 点击 WindowPreferences ,左侧应有 Scala 选项

5.2 创建Scala项目

步骤一:新建Scala项目

  1. 点击 FileNewOther...
  2. 在列表中找到 Scala WizardsScala Project
  3. 点击 Next
  4. 输入项目名称,如 ScalaDemo
  5. 点击 Finish

步骤二:创建Scala对象

  1. 在项目的 src 目录上右键 → NewScala Object (或 Scala Class
  2. 输入名称,如 HelloWorld
  3. 点击 Finish

步骤三:编写并运行代码

scala 复制代码
// HelloWorld.scala
// 创建在 src 目录下的默认包中

object HelloWorld {
  def main(args: Array[String]): Unit = {
    // 打印Hello World到控制台
    println("Hello, Scala in Eclipse!")
    
    // 简单的变量和运算
    val a = 10
    val b = 20
    println(s"$a + $b = ${a + b}")
  }
}

步骤四:运行

  1. 在代码编辑区右键 → Run AsScala Application

  2. 控制台将显示运行结果:

    复制代码
    Hello, Scala in Eclipse!
    10 + 20 = 30

六、使用IntelliJ IDEA创建Scala项目

6.1 IDEA中安装Scala插件

步骤一:打开插件管理

  1. 打开IntelliJ IDEA
  2. 点击菜单 FileSettings (Mac上是 IntelliJ IDEAPreferences
  3. 在左侧导航中找到 Plugins
  4. 确保选择 Marketplace 标签页

步骤二:搜索并安装Scala插件

  1. 在搜索框中输入 "Scala"
  2. 找到 "Scala" 插件(作者是 JetBrains)
  3. 点击 Install 按钮
  4. 等待安装完成
  5. 安装完成后,点击 Restart IDE 重启IDEA

步骤三:验证插件安装

  1. 重启IDEA后,进入 SettingsPluginsInstalled
  2. 确认 Scala 插件已启用(勾选状态)
  3. 点击 FileNewProject ,应能看到 Scala 选项

6.2 创建Scala项目

步骤一:新建项目

  1. 点击 FileNewProject
  2. 在左侧选择 ScalaIDEA
  3. 配置以下选项:
    • Name :项目名称,如 ScalaProject
    • Location:项目存放路径
    • JDK:选择已安装的JDK(如JDK 8)
    • Scala SDK :点击 Create → 选择Scala版本(如2.12.18)
      • 如果本地没有安装Scala,可以点击 Download 下载
  4. 点击 Finish

步骤二:配置Scala SDK(如果未配置)

  1. 点击 FileProject Structure (快捷键 Ctrl+Alt+Shift+S
  2. 选择 Global Libraries
  3. 点击 +Scala SDK
  4. 选择本地安装的Scala,或点击 Download 下载
  5. 选择版本(如 2.12.18),点击 OK
  6. 点击 ApplyOK

步骤三:创建Scala文件

  1. src 目录上右键 → NewScala Class
  2. 选择类型:
    • Object:单例对象(常用于包含main方法的入口)
    • Class:类
    • Trait:特质
  3. 输入名称,如 HelloWorld

步骤四:编写代码

scala 复制代码
// 在src目录下创建的HelloWorld.scala文件
// 包名根据目录结构自动生成

package com.scala.demo

/**
 * HelloWorld 入门程序
 * 演示在IDEA中创建和运行Scala项目
 */
object HelloWorld {
  
  /**
   * 程序入口方法
   * @param args 命令行参数数组
   */
  def main(args: Array[String]): Unit = {
    // 打印欢迎信息
    println("Hello, Scala in IntelliJ IDEA!")
    
    // 演示基本功能
    val language = "Scala"
    val version = "2.12"
    println(s"学习$language $version 版本")
    
    // 调用自定义方法
    val result = add(10, 20)
    println(s"10 + 20 = $result")
    
    // 使用集合操作
    val numbers = List(1, 2, 3, 4, 5)
    val sum = numbers.reduce(_ + _)
    println(s"列表求和: $sum")
  }
  
  /**
   * 加法方法
   * @param a 第一个加数
   * @param b 第二个加数
   * @return 两数之和
   */
  def add(a: Int, b: Int): Int = {
    a + b  // 返回a和b的和
  }
}

步骤五:运行程序

  1. main 方法左侧会出现绿色的 运行按钮(绿色三角形)

  2. 点击该按钮 → 选择 Run 'HelloWorld'

  3. 或者右键点击文件 → Run 'HelloWorld'

  4. 底部控制台显示运行结果:

    复制代码
    Hello, Scala in IntelliJ IDEA!
    学习Scala 2.12 版本
    10 + 20 = 30
    列表求和: 15

步骤六:使用IDEA的Scala Worksheet(工作表)

  1. src 目录上右键 → NewScala Worksheet

  2. 输入名称,如 TestWorksheet

  3. 在工作表中输入代码(可以即时看到结果):

    scala 复制代码
    object TestWorksheet {
      // 代码执行后,右侧会自动显示结果
      1 + 1                        // 结果显示在代码右侧:res0: Int = 2
      "Hello" + " " + "Scala"      // res1: String = "Hello Scala"
      List(1,2,3).map(_ * 2)       // res2: List[Int] = List(2, 4, 6)
    }
  4. 保存文件(Ctrl+S),右侧自动显示计算结果


七、综合实战案例

案例1:学生成绩管理系统

scala 复制代码
import scala.collection.mutable.ListBuffer

// 定义学生类
class StudentInfo(val id: Int, val name: String) {
  // 使用ListBuffer存储各科成绩
  private val scores = ListBuffer[(String, Double)]()  // (科目名, 分数)
  
  // 添加成绩
  def addScore(subject: String, score: Double): Unit = {
    scores += ((subject, score))   // 添加科目和分数的元组
    println(s"$name 的 $subject 成绩已录入: $score")
  }
  
  // 计算平均分
  def averageScore(): Double = {
    if (scores.isEmpty) 0.0         // 没有成绩时返回0
    else scores.map(_._2).sum / scores.length  // 所有分数求和再除以数量
  }
  
  // 获取最高分
  def highestScore(): (String, Double) = {
    if (scores.isEmpty) ("无", 0.0)
    else scores.maxBy(_._2)         // 按分数取最大值
  }
  
  // 获取最低分
  def lowestScore(): (String, Double) = {
    if (scores.isEmpty) ("无", 0.0)
    else scores.minBy(_._2)         // 按分数取最小值
  }
  
  // 显示成绩单
  def showReport(): Unit = {
    println(s"\n===== ${name}的成绩单 (学号: $id) =====")
    for ((subject, score) <- scores) {
      // 使用模式匹配解构元组
      val grade = score match {
        case s if s >= 90 => "优秀"
        case s if s >= 80 => "良好"
        case s if s >= 70 => "中等"
        case s if s >= 60 => "及格"
        case _            => "不及格"
      }
      println(f"  $subject%-10s $score%6.1f  ($grade)")
    }
    println(f"  平均分: ${averageScore()}%.1f")
    val (hSub, hScore) = highestScore()
    val (lSub, lScore) = lowestScore()
    println(f"  最高分: $hSub ($hScore%.1f)")
    println(f"  最低分: $lSub ($lScore%.1f)")
    println("=" * 40)
  }
}

// 定义班级管理类
class ClassManager(val className: String) {
  private val students = ListBuffer[StudentInfo]()  // 学生列表
  
  // 添加学生
  def addStudent(id: Int, name: String): StudentInfo = {
    val student = new StudentInfo(id, name)  // 创建学生对象
    students += student                       // 添加到列表
    student                                   // 返回学生对象
  }
  
  // 按平均分排名
  def rankByAverage(): List[StudentInfo] = {
    students.sortBy(-_.averageScore()).toList  // 按平均分降序排列
  }
  
  // 显示班级总览
  def showClassReport(): Unit = {
    println(s"\n======== $className 班级总览 ========")
    val ranked = rankByAverage()              // 获取排名
    
    for ((student, index) <- ranked.zipWithIndex) {
      // zipWithIndex 将元素和索引配对:List((elem0,0), (elem1,1), ...)
      println(f"  第${index + 1}%-2d名: ${student.name}%-6s 平均分: ${student.averageScore()}%.1f")
    }
    
    // 班级平均分
    val classAvg = ranked.map(_.averageScore()).sum / ranked.length
    println(f"\n  班级平均分: $classAvg%.1f")
    
    // 及格率
    val passCount = ranked.count(_.averageScore() >= 60)
    val passRate = passCount.toDouble / ranked.length * 100
    println(f"  及格率: $passRate%.1f%%")
    println("=" * 45)
  }
}

object StudentScoreDemo {
  def main(args: Array[String]): Unit = {
    
    // 创建班级管理器
    val classMgr = new ClassManager("2024级计算机1班")
    
    // 添加学生
    val s1 = classMgr.addStudent(1001, "张三")
    val s2 = classMgr.addStudent(1002, "李四")
    val s3 = classMgr.addStudent(1003, "王五")
    val s4 = classMgr.addStudent(1004, "赵六")
    
    // 录入成绩
    s1.addScore("数学", 92)
    s1.addScore("英语", 85)
    s1.addScore("编程", 98)
    
    s2.addScore("数学", 78)
    s2.addScore("英语", 82)
    s2.addScore("编程", 88)
    
    s3.addScore("数学", 65)
    s3.addScore("英语", 70)
    s3.addScore("编程", 72)
    
    s4.addScore("数学", 55)
    s4.addScore("英语", 60)
    s4.addScore("编程", 58)
    
    // 显示个人成绩单
    s1.showReport()
    s2.showReport()
    s3.showReport()
    s4.showReport()
    
    // 显示班级总览
    classMgr.showClassReport()
  }
}