18scala笔记

Scala2.12

视频地址

1 入门

1.1 发展历史

1.2 Scala 和 Java

Scala = Java++

  1. 编写代码
  2. 使用scalac编译成.class字节码文件
  3. scala + .class文件 执行代码

image-20230213150532260

1.3 特点

image-20230213150810221

1.4 安装

视频地址

注意配置好环境变量

简单代码

image-20230213152120101

1.5 编译文件

编译scala文件会产生两个.class文件

image-20230213152410818

使用java命令执行scala编译出的class文件,报错:

image-20230213152629543

正确方法如下

image-20230213153254448

1.6 IDEA使用

创建maven项目

image-20230213153900096

安装scala插件

image-20230213154308682

创建java 和scala目录同时设置成源代码目录

image-20230213154551564

右击项目名 -> 添加框架支持

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.7 HelloWorld

创建包和类 输入main即可自动补全

image-20230213155506439

基本格式

image-20230213155742597

1.8 class 和 object

static关键字 调用方法:类名.属性

// 在java中 static修饰的字段是属于类的,也就是所有创建的对象都会有这个属性
public class Student {private String name;private Integer age;private static String school = "atguigu";public Student(String name, Integer age) {this.name = name;this.age = age;}public void printInfo(){System.out.println(this.name + " " + this.age + " " + Student.school);}public static void main(String[] args) {Student alice = new Student("alice", 20);Student bob = new Student("bob", 23);alice.printInfo();bob.printInfo();}
}

而在scala中,使用伴生对象代替了static字段,也就是类存在,伴生对象就存在

class Student(name: String, age: Int) {def printInfo(): Unit = {println(name + " " + age + " " + Student.school)}
}// 引入伴生对象
object Student{val school: String = "atguigu"def main(args: Array[String]): Unit = {val alice = new Student("alice", 20)val bob = new Student("bob", 23)alice.printInfo()bob.printInfo()}
}

image-20230213165715060

1.9 反编译

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2 变量和数据类型

2.1 注释

和java完全一样

2.2 变量和常量

image-20230213170334750

原则

  1. 声明变量时,类型可以省略,编译器自动推导,即类型推导,var a1 = 10
  2. 类型确定后,就不能修改,说明Scala是强数据类型语言
  3. 变量声明时,必须要有初始值
  4. 在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变,val修饰的变量不可改。

2.3 命名规范

基本与Java一致

  1. 以字母或者下划线开头,后接字母、数字、下划线
  2. 以操作符开头,且只包含操作符(+ - * / # !等)
  3. 用反引号`…`包括的任意字符串,即使是 Scala 关键字(39 个)也可以

关键字

image-20230213202819315

2.4 字符串输出

基本用法

  1. 字符串通过+连接
  2. printf 用法:字符串,通过%传值。
  3. 字符串模板(插值字符串):通过$获取变量值
var name: String = "jinlian"var age: Int = 18//(1)字符串,通过+号连接println(name + " " + age)
//(2)printf 用法字符串,通过%传值。printf("name=%s age=%d\n", name, age)
//(3)字符串,通过$引用
/*多行字符串,在 Scala中,利用三个双引号包围多行字符串就可以实现。输入的内容,带有空格、\t 之类,导致每一行的开始位置不能整洁对齐。应用 scala 的 stripMargin 方法,在 scala 中 stripMargin 默认是“|”作为连接符,在多行换行的行头前面加一个“|”符号即可。
*/
val s ="""|select|name,|age|from user|where name="zhangsan"""".stripMargin
println(s)//如果需要对变量进行运算,那么可以加${}
val s1 =s"""|select| name,| age|from user|where name="$name" and age=${age+2}""".stripMarginprintln(s1)val s2 = s"name=$name"println(s2)

image-20230213203651814

2.5 键盘输入

基本用法

  • StdIn.readLine()

  • StdIn.readShort()

  • StdIn.readDouble()

    // 1 输入姓名println("input name:")var name = StdIn.readLine()// 2 输入年龄println("input age:")var age = StdIn.readShort()// 3 输入薪水println("input sal:")var sal = StdIn.readDouble()// 4 打印println("name=" + name)println("age=" + age)println("sal=" + sal)

image-20230213203914967

2.6 数字类型

Java

image-20230213204030994

scala

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.7 整数类型

Byte、Short、Int、Long

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

定义不要超过类型的范围

// 正确var n1:Byte = 127var n2:Byte = -128// 错误// var n3:Byte = 128// var n4:Byte = -129

Scala 的整型,默认为 Int 型,声明 Long 型,须后加‘l’或‘L’

var n5 = 10
println(n5)
var n6 = 9223372036854775807L
println(n6)

Scala 程序中变量常声明为 Int 型,除非不足以表示大数,才使用 Long

2.8 浮点类型

Scala 的浮点类型可以表示一个小数,比如 123.4f,7.8,0.12 等等。

image-20230213205125576

Scala 的浮点型常量默认为 Double 型,声明 Float 型常量,须后加‘f’或‘F’。

// 建议,在开发中需要高精度小数时,请选择 Doublevar n7 = 2.2345678912fvar n8 = 2.2345678912

image-20230213205227428

2.9 字符类型

字符类型可以表示单个字符,字符类型是 Char。

  1. 字符常量是用单引号 ’ ’ 括起来的单个字符。
  2. \t :一个制表位,实现对齐的功能
  3. \n :换行符
  4. \\ :表示\
  5. \" :表示"

虽然IDEA会报错 但是默认会自动进行强制类型转换

image-20230213205758743

这样写就会报错

image-20230213205845062

2.10 Boolean类型

同Java

image-20230213205927862

2.11 Unit 、Null 、 Nothing

image-20230213210100741

Unit

image-20230213210324788

Null 类只有一个实例对象,Null 类似于 Java 中的 null 引用。Null 可以赋值给任 意引用类型(AnyRef),但是不能赋值给值类型(AnyVal

object TestDataType {def main(args: Array[String]): Unit = {//null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)var cat = new Cat();cat = null // 正确var n1: Int = null // 错误println("n1:" + n1)}
}
class Cat {
}

Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方 法不会正常返回,而且由于 Nothing 是其他任意类型的子类,他还能跟要求返回值的方法兼容。

 def main(args: Array[String]): Unit = {def test() : Nothing={throw new Exception()}test()}

image-20230213210823299

2.12 类型转换

基本同Java

object TestValueTransfer {def main(args: Array[String]): Unit = {//(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有
数据转换成精度大的那种数值类型,然后再进行计算。var n = 1 + 2.0println(n) // n 就是 Double//(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就
会进行自动类型转换。var n2 : Double= 1.0//var n3 : Int = n2 //错误,原因不能把高精度的数据直接赋值和低
精度。//(3)(byte,short)和 char 之间不会相互自动转换。var n4 : Byte = 1//var c1 : Char = n4 //错误var n5:Int = n4//(4)byte,short,char 他们三者可以计算,在计算时首先转换为 int
类型。var n6 : Byte = 1var c2 : Char = 1// var n : Short = n6 + c2 //当 n6 + c2 结果类型就是 int// var n7 : Short = 10 + 90 //错误}
}

强制类型转换

Java : int num = (int)2.5
Scala : var num : Int = 2.7.toIntvar r1: Int = 10 * 3.5.toInt + 6 * 1.5.toInt // 10 *3 + 6*1 = 36
var r2: Int = (10 * 3.5 + 6 * 1.5).toInt // 44.0.toInt = 44

数值类型与String类型转换

  1. 基本类型转 String 类型(语法:将基本类型的值+“” 即可)
  2. String 类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)

def main(args: Array[String]): Unit = {var n: Int = 130var b: Byte = n.toByteprintln(b) //-126 原因:Byte最大值为127,-128 -127 -126
}

3 运算符

大体同Java

3.1 注意事项

Scala:==更加类似于 Java 中的 equals

def main(args: Array[String]): Unit = {val s1 = "abc"val s2 = new String("abc")println(s1 == s2)
println(s1.eq(s2))
}
输出结果:
true
false

Scala 中没有++--操作符,可以通过+=-=来实现同样的效果

3.2 运算符本质

在 Scala 中其实是没有运算符的,所有运算符都是方法。

  1. 当调用对象的方法时,点.可以省略
  2. 如果函数参数只有一个,或者没有参数,()可以省略
object TestOpt {def main(args: Array[String]): Unit = {// 标准的加法运算val i:Int = 1.+(1)// (1)当调用对象的方法时,.可以省略val j:Int = 1 + (1)// (2)如果函数参数只有一个,或者没有参数,()可以省略val k:Int = 1 + 1println(1.toString())println(1 toString())println(1 toString)}
}

4 流程控制

Scala 中 if else 表达式其实是有返回值的,具体返回值取决于满足条件的 代码体的最后一行内容。

object TestIfElse {def main(args: Array[String]): Unit = {println("input age")var age = StdIn.readInt()val res :String = if (age < 18){"童年"}else if(age>=18 && age<30){"中年"}else{"老年"}println(res)}
}

Java 中的三元运算符可以用 if else 实现

如果大括号{}内的逻辑代码只有一行,大括号可以省略。如果省略大括号,if 只对最近 的一行逻辑代码起作用。

object TestIfElse {def main(args: Array[String]): Unit = {// Java
// int result = flag?1:0// Scalaprintln("input age")var age = StdIn.readInt()val res:Any = if (age < 18) "童年" else "成年"println(res)}
}

4.1 Switch 分支结构

在 Scala 中没有 Switch,而是使用模式匹配来处理

4.2 For循环控制

基本用法 to

// i 将会从 1-3 循环,前后闭合
for(i <- 1 to 3){print(i + " ")
}
i 1 2 3

Until

// 前闭合后开
for(i <- 1 until 3) {print(i + " ")
}
i 1 2

4.3 循环守卫

for(i <- 1 to 3 if i != 2) {print(i + " ")
}
println()
// 等价于 =============
for (i <- 1 to 3){
if (i != 2) {print(i + " ")
}
}

4.4 循环步长

// by 表示步长
for (i <- 1 to 10 by 2) {println("i=" + i)
}
i 1 3 5 7 9

4.5 嵌套循环

// 没有关键字,所以范围后一定要加;来隔断逻辑
for(i <- 1 to 3; j <- 1 to 3) {println(" i =" + i + " j = " + j)
}
// 等价于===============================
for (i <- 1 to 3) {for (j <- 1 to 3) {println("i =" + i + " j=" + j)}
}

4.6 引入变量

for(i <- 1 to 3; j = 4 - i) {println("i=" + i + " j=" + j)
}
==================================
for {i <- 1 to 3j = 4 - i
} {println("i=" + i + " j=" + j)
}
==================================
for (i <- 1 to 3) {var j = 4 - iprintln("i=" + i + " j=" + j)
}
// 三者等价

4.7 循环返回值 yield

类似于js中的map

// 将遍历过程中处理的结果返回到一个新 Vector 集合中
val res = for(i <- 1 to 10) yield i
println(res)
// 结果:res = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// 将原数据中所有值乘以 2,并把数据返回到一个新的集合中
var res = for(i <-1 to 10) yield {i * 2}
// 结果:res = Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

4.8 倒序打印 reverse

for(i <- 1 to 10 reverse){println(i)
}

4.9 While循环控制

  1. 与 for 语句不同,while 语句没有返回值,即整个 while 语句的结果是 Unit 类型()
  2. 因为 while 中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在 while 循环的外部,那么就等同于循环的内部对外部的变量 造成了影响,所以不推荐使用,而是推荐使用 for 循环。

显然,while循环不能满足大数据并行处理的要求,因为他们公用的都是外部的变量,比如控制循环的i

def main(args: Array[String]): Unit = {var i = 0//变量必须在while外面声明while (i < 10) {println("宋宋,喜欢海狗人参丸" + i)i += 1}
}

4.10 循环中断

Scala 内置控制结构特地去掉了 break 和 continue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable 控制结构来实现 break continue 功能。

采用异常的方式退出循环

def main(args: Array[String]): Unit = {try {for (elem <- 1 to 10) {println(elem)if (elem == 5) throw new RuntimeException}} catch {case e =>}println("正常结束循环")
}

采用 Scala 自带的函数,退出循环

import scala.util.control.Breaks
def main(args: Array[String]): Unit = {Breaks.breakable(for (elem <- 1 to 10) {println(elem)if (elem == 5) Breaks.break()})println("正常结束循环")
}

对 break 进行省略

import scala.util.control.Breaks._
object TestBreak {def main(args: Array[String]): Unit = {breakable {for (elem <- 1 to 10) {println(elem)if (elem == 5) break}}println("正常结束循环")}
}

5 函数式编程

面向对象编程

解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。

  • 对象:用户

  • 行为:登录、连接 JDBC、读取数据库

  • 属性:用户名、密码

Scala 语言是一个完全面向对象编程语言。

万物皆对象

对象的本质:对数据行为的一个封装

函数式编程

解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。

例如:请求->用户名、密码->连接 JDBC->读取数据库

Scala 语言是一个完全函数式编程语言。万物皆函数

函数的本质:函数可以当做一个值进行传递

5.1 函数基础

5.1.1 函数基本语法

image-20230214132525910

// 定义一个函数,实现将传入的名称打印出来。
object TestFunction {
def main(args: Array[String]): Unit = {// (1)函数定义def f(arg: String): Unit = {println(arg)}// (2)函数调用// 函数名(参数)f("hello world")}
}
5.1.2 函数和方法的区别

概念

  1. 为完成某一功能的程序语句的集合,称为函数。
  2. 类中的函数称之方法。

案例实操

  1. Scala 语言可以在任何的语法结构中声明任何的语法
  2. 函数没有重载和重写的概念;方法可以进行重载重写
  3. Scala 中函数可以嵌套定义
// (2)方法可以进行重载和重写,程序可以执行
def main(): Unit = {
}def main(args: Array[String]): Unit = {
// (1)Scala 语言可以在任何的语法结构中声明任何的语法
import java.util.Date
new Date()// (2)函数没有重载和重写的概念,程序报错
def test(): Unit = {println("无参,无返回值")
}
test()
def test(name: String): Unit = {println()
}//(3)Scala 中函数可以嵌套定义
def test2(): Unit = {def test3(name: String): Unit = {println("函数可以嵌套定义")}
}
}
5.1.3 函数定义
 def main(args: Array[String]): Unit = {// 函数 1:无参,无返回值def test1(): Unit ={println("无参,无返回值")}test1()// 函数 2:无参,有返回值def test2():String={return "无参,有返回值"}println(test2())// 函数 3:有参,无返回值def test3(s:String):Unit={println(s)}test3("jinlian")// 函数 4:有参,有返回值def test4(s:String):String={return s+"有参,有返回值"}println(test4("hello "))// 函数 5:多参,无返回值def test5(name:String, age:Int):Unit={println(s"$name, $age")}test5("dalang",40)}
}
5.1.4 参数函数
  1. 可变参数
  2. 如果参数列表中存在多个参数,那么可变参数一般放置在最后
  3. 参数默认值,一般将有默认值的参数放置在参数列表的后面
  4. 带名参数
object TestFunction {def main(args: Array[String]): Unit = {// (1)可变参数def test( s : String* ): Unit = {println(s)}// 有输入参数:输出 Arraytest("Hello", "Scala")// 无输入参数:输出 List()test()=======================================================// (2)如果参数列表中存在多个参数,那么可变参数一般放置在最后def test2( name : String, s: String* ): Unit = {println(name + "," + s)}test2("jinlian", "dalang")=======================================================// (3)参数默认值def test3( name : String, age : Int = 30 ): Unit = {println(s"$name, $age")}// 如果参数传递了值,那么会覆盖默认值test3("jinlian", 20)// 如果参数有默认值,在调用的时候,可以省略这个参数test3("dalang")// 一般情况下,将有默认值的参数放置在参数列表的后面def test4( sex : String = "男", name : String ): Unit = {println(s"$name, $sex")}
// Scala 函数中参数传递是,从左到右//test4("wusong") =======================================================    //(4)带名参数test4(name="ximenqing")}
}
5.1.5 函数至简原则

能省就省

  1. return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
  2. 如果函数体只有一行代码,可以省略花括号
  3. 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
  4. 如果有 return,则不能省略返回值类型,必须指定
  5. 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
  6. Scala 如果期望是无返回值类型,可以省略等号
  7. 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
  8. 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
  9. 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
object TestFunction {def main(args: Array[String]): Unit = {// (0)函数标准写法def f( s : String ): String = {return s + " jinlian"}println(f("Hello"))// 至简原则:能省则省//(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值def f1( s : String ): String = {s + " jinlian"}println(f1("Hello"))//(2)如果函数体只有一行代码,可以省略花括号def f2(s:String):String = s + " jinlian"//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)def f3( s : String ) = s + " jinlian"println(f3("Hello3"))//(4)如果有 return,则不能省略返回值类型,必须指定。def f4() :String = {return "ximenqing4"}println(f4())//(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用def f5(): Unit = {return "dalang5"}println(f5())//(6)一般不使用 Scala 如果期望是无返回值类型,可以省略等号// 将无返回值的函数称之为过程def f6() {"dalang6"}println(f6())  //输出值为(),因为没有等号 是无返回值类型//(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加def f7() = "dalang7"println(f7())println(f7)//(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略def f8 = "dalang"//println(f8())println(f8)//(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略//这就是匿名函数 lambda表达式(x:String)=>{println("wusong")}}

5.2 函数高级

5.2.1 高阶函数

函数作为参数输入

image-20230214144049585

_的用法

(3) f中传入一个函数,函数的参数为name,方法体为输出name

(4) 可以知道f中传入的函数就是将一个参数输出,无论参数的名字叫什么,所以可以用_代替参数名

image-20230410141509421

    // 2. 函数作为参数进行传递// 定义二元计算函数def dualEval(op: (Int, Int)=>Int, a: Int, b: Int): Int = {op(a, b)}def add(a: Int, b: Int): Int = {a + b}println(dualEval(add, 12, 35))println(dualEval((a, b) => a + b, 12, 35))println(dualEval(_ + _, 12, 35))

函数作为值进行传递,即一个函数等于另外一个函数,f2 = f _表示f2这个函数赋值为f

image-20230214150629164

image-20230214150702524

函数作为函数的返回值返回

// 函数f5的参数为空,它的返回值为一个输入参数为Int,返回值为空的函数
def f5(): Int=>Unit = {def f6(a: Int): Unit = {println("f6调用 " + a)}f6    // 将函数直接返回
}
// 调用函数,相当于f5()返回的是一个函数名,后面的(25)是参数
f5()(25)
5.2.2 匿名函数
(x:Int)=>{函数体}

至简原则

  • 参数的类型可以省略,会根据形参进行自动的推导

image-20230214144655828

  • 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。

image-20230214144715540

  • 匿名函数如果只有一行,则大括号也可以省略

image-20230214144753196

  • 如果参数只出现一次,则参数省略且后面参数可以用_代替

image-20230214144848182

  • 如果执行的是一步操作,那么可以直接省略_

image-20230214145019165

image-20230214145356526

简化:

image-20230214145641473

5.2.3 实战

对数组进行操作,定义运算函数

  def main(args: Array[String]): Unit = {val arr: Array[Int] = Array(12, 45, 75, 98)// 对数组进行处理,将操作抽象出来,处理完毕之后的结果返回一个新的数组def arrayOperation(array: Array[Int], op: Int=>Int): Array[Int] = {for (elem <- array) yield op(elem)}// 定义一个加一操作def addOne(elem: Int): Int = {elem + 1}// 调用函数val newArray: Array[Int] = arrayOperation(arr, addOne)// 将数组中的元素取出来 并用,分隔println(newArray.mkString(","))// 传入匿名函数,实现元素翻倍val newArray2 = arrayOperation(arr, _ * 2)// 将数组中的元素取出来 并用,分隔println(newArray2.mkString(", "))}

image-20230214152813936

定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。要求调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true。

    val fun = (i: Int, s: String, c: Char) => {if (i == 0 && s == "" && c == '0') false else true}println(fun(0, "", '0'))println(fun(0, "", '1'))println(fun(23, "", '0'))println(fun(0, "hello", '0'))println("===========================")

定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。 它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接 收一个 Char 类型的参数,返回一个 Boolean 的值。 要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。

    def func(i: Int): String=>(Char=>Boolean) = {def f1(s: String): Char=>Boolean = {def f2(c: Char): Boolean = {if (i == 0 && s == "" && c == '0') false else true}f2}f1}println(func(0)("")('0'))println(func(0)("")('1'))println(func(23)("")('0'))println(func(0)("hello")('0'))// 匿名函数简写// 匿名函数接收String类型的参数s,返回值为一个函数// 匿名函数接收Char类型的参数c,返回值为Booleandef func1(i: Int): String => (Char => Boolean) = {s => {c => {if (i == 0 && s == "" && c == '0') false else true}}}
// =======================省去括号就变成下面的样子def func1(i: Int): String=>(Char=>Boolean) = {s => c => if (i == 0 && s == "" && c == '0') false else true}println(func1(0)("")('0'))println(func1(0)("")('1'))println(func1(23)("")('0'))println(func1(0)("hello")('0'))// 柯里化def func2(i: Int)(s: String)(c: Char): Boolean = {if (i == 0 && s == "" && c == '0') false else true}println(func2(0)("")('0'))println(func2(0)("")('1'))println(func2(23)("")('0'))println(func2(0)("hello")('0'))}
5.2.4 函数柯里化和闭包

闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

原理: scala是面向对象的,所有的方法都有一个地址,即:在堆中保存了下来,所以即使先执行的方法出栈了,但其数据任然保存在堆中,后面执行的方法可以访问到数据

object TestFunction {def main(args: Array[String]): Unit = {def f1()={var a:Int = 10def f2(b:Int)={a + b}f2 _}// 在调用时,f1 函数执行完毕后,局部变量 a 应该随着栈空间释放掉val f = f1()// 但是在此处,变量 a 其实并没有释放,而是包含在了 f2 函数的内部,形成了闭合的效果println(f(3))println(f1()(3))

函数柯里化:把一个参数列表的多个参数,变成多个参数列表。

// 函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存在闭包def f3()(b:Int)={a + b}println(f3()(3))
5.2.5 递归

同Java

5.2.6 控制抽象

值调用:参数传值,平常的传值方法

def foo(a: Int):Unit = {println(a) }

名调用:传名参数,把代码块传递过去,有返回值

     def f1(): Int = {println("f1调用")12}
// 2. 传名参数,传递的不再是具体的值,而是代码块def f2(a: =>Int): Unit = {println("a: " + a)println("a: " + a)}// 传递的是f1这个代码块,相当于f2中的参数a=f1()f2(f1())f2({println("这是一个代码块")29})

image-20230214170241120

案例:自定义while循环

使用柯里化最容易理解,while(代码块)(代码块)

package chapter05object Test12_MyWhile {def main(args: Array[String]): Unit = {var n = 10// 1. 常规的while循环while (n >= 1){println(n)n -= 1}// 2. 用闭包实现一个函数,将代码块作为参数传入,递归调用def myWhile(condition: =>Boolean): (=>Unit)=>Unit = {// 内层函数需要递归调用,参数就是循环体def doLoop(op: =>Unit): Unit = {if (condition){opmyWhile(condition)(op)}}doLoop _}println("=================")n = 10myWhile(n >= 1){println(n)n -= 1}// 3. 用匿名函数实现def myWhile2(condition: =>Boolean): (=>Unit)=>Unit = {// 内层函数需要递归调用,参数就是循环体op => {if (condition){opmyWhile2(condition)(op)}}}println("=================")n = 10myWhile2(n >= 1){println(n)n -= 1}// 3. 用柯里化实现====最容易理解def myWhile3(condition: =>Boolean)(op: =>Unit): Unit = {if (condition){opmyWhile3(condition)(op)}}println("=================")n = 10myWhile3(n >= 1){println(n)n -= 1}}
}
5.2.7 惰性加载

当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函 数才会执行。这种函数我们称之为惰性函数。

def main(args: Array[String]): Unit = {lazy val res = sum(10, 30)println("----------------")println("res=" + res)
}
def sum(n1: Int, n2: Int): Int = {println("sum 被执行。。。")return n1 + n2
}

image-20230214175308647

6 面向对象

Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。

Scala 中语法和 Java 不同,补充了更多的功能。

6.1 Scala包

6.1.1 包的命名

同java

6.1.2 包语句

有两种风格

  1. com.jaken.scala
  2. 嵌套风格
package com{package jaken{package scala{}}
}

优点:

  1. 一个源文件中可以声明多个 package
  2. 子包中的类可以直接访问父包中的内容,而无需导包
package com {import com.atguigu.Inner //父包访问子包需要导包object Outer {val out: String = "out"def main(args: Array[String]): Unit = {println(Inner.in)}}package atguigu {object Inner {val in: String = "in"def main(args: Array[String]): Unit = {println(Outer.out) //子包访问父包无需导包}}}}package other {
}
6.1.3 包对象

在 Scala 中可以为每个包定义一个同名的包对象(package object),定义在包对象中的成员,作为其对应包下所有 class 和 object 的共享变量,可以被直接访问

package object com{val shareValue="share"def shareMethod()={}
}package com {object Outer {val out: String = "out"def main(args: Array[String]): Unit = {// 可以直接拿到包对象println(shareValue)}}
}
6.1.4 导包说明

同java

6.2 类和对象

:可以看成一个模板

对象:表示具体的事物

6.2.1 定义类和属性

Scala 中没有 public,个.scala 中可以写个类。

  1. Scala 语法中,类并不声明为 public,所有这些类都具有公有可见性(即默认就是 public)
  2. 一个 Scala 源文件可以包含多个类
class Person {var name: String = "bobo" //定义属性var age: Int = _ // _表示给属性一个默认值,Int默认为0,String为null//Bean 属性(@BeanProperty),修饰的属性有getter/setter@BeanProperty var sex: String = "男"}
object Person {def main(args: Array[String]): Unit = {var person = new Person()println(person.name)person.setSex("女")println(person.getSex)}
}

6.3 封装

Scala 中的 public 属性,底层实际为 private,并通过 get 方法(obj.field())和 set 方法 (obj.field_=(value))对其进行操作。

所以 Scala 并不推荐将属性设为 private,再为其设置 public 的 get 和 set 方法的做法。但由于很多 Java 框架都利用反射调用 getXXX 和 setXXX 方 法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX 和 setXXX 方法(通过 @BeanProperty 注解实现)。

6.3.1 访问权限
  1. Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。
  2. private 为私有权限,只在类的内部和伴生对象中可用。
  3. protected 为受保护权限,Scala 中受保护权限比 Java 中更严格同类子类可以访问,同包无法访问
  4. private[包名]增加包访问权限,包名下的其他类也可以使用

image-20230410161637503

在子类中idCard由于是私有属性 所以不能访问

image-20230410161956170

在实例对象中,protect对象不能访问

image-20230410162129222

6.3.2 构造方法
object Test05_Constructor {def main(args: Array[String]): Unit = {// 调用的主构造方法val student1 = new Student1//调用的一般方法student1.Student1()//调用的辅助构造方法1val student2 = new Student1("alice")//调用的辅助构造方法2val student3 = new Student1("bob", 25)}
}// 定义一个类,其实也是主构造方法
class Student1() {// 定义属性var name: String = _var age: Int = _println("1. 主构造方法被调用")// 声明辅助构造方法1def this(name: String) {// 首先必须调用主构造器this()    println("2. 辅助构造方法一被调用")this.name = nameprintln(s"name: $name age: $age")}// 声明辅助构造方法2def this(name: String, age: Int){//调用构造方法1this(name)println("3. 辅助构造方法二被调用")this.age = ageprintln(s"name: $name age: $age")}
// 不同于JAVA,这并不是一个构造方法,而是一个普通的方法def Student1(): Unit = {println("一般方法被调用")}
}

运行结果

image-20230413204137491

6.3.3构造方法参数

image-20230413204426920

实操

object Test06_ConstructorParams {def main(args: Array[String]): Unit = {val student2 = new Student2student2.name = "alice"student2.age = 18println(s"student2: name = ${student2.name}, age = ${student2.age}")val student3 = new Student3("bob", 20)println(s"student3: name = ${student3.name}, age = ${student3.age}")val student4 = new Student4("cary", 25)//由于Student4的参数未修饰,所以参数是局部变量而不是成员属性
//    println(s"student4: name = ${student4.name}, age = ${student4.age}")student4.printInfo()val student5 = new Student5("bob", 20)println(s"student3: name = ${student5.name}, age = ${student5.age}")student3.age = 21val student6 = new Student6("cary", 25, "atguigu")println(s"student6: name = ${student6.name}, age = ${student6.age}")student6.printInfo()}
}// 定义类
// 无参构造器
class Student2 {// 单独定义属性var name: String = _var age: Int = _
}// 上面定义等价于
class Student3(var name: String, var age: Int)// 主构造器参数无修饰,name和age属性就相当于局部变量,而不是成员属性
class Student4(name: String, age: Int){def printInfo(){println(s"student4: name = ${name}, age = $age")}
}//class Student4(_name: String, _age: Int){
//  var name: String = _name
//  var age: Int = _age
//}
//用常量修饰
class Student5(val name: String, val age: Int)
//用变量修饰
class Student6(var name: String, var age: Int){var school: String = _def this(name: String, age: Int, school: String){this(name, age)this.school = school}def printInfo(){println(s"student6: name = ${name}, age = $age, school = $school")}
}

运行结果

image-20230413205050433

6.4 继承

image-20230413205200392
object Test07_Inherit {def main(args: Array[String]): Unit = {val student1: Student7 = new Student7("alice", 18)val student2 = new Student7("bob", 20, "std001")student1.printInfo()student2.printInfo()val teacher = new Teacherteacher.printInfo()def personInfo(person: Person7): Unit = {person.printInfo()}println("=========================")val person = new Person7personInfo(student1)personInfo(teacher)personInfo(person)}
}// 定义一个父类
class Person7() {var name: String = _var age: Int = _println("1. 父类的主构造器调用")def this(name: String, age: Int){this()println("2. 父类的辅助构造器调用")this.name = namethis.age = age}def printInfo(): Unit = {println(s"Person: $name $age")}
}// 定义子类
class Student7(name: String, age: Int) extends Person7(name, age) {var stdNo: String = _println("3. 子类的主构造器调用")def this(name: String, age: Int, stdNo: String){this(name, age)println("4. 子类的辅助构造器调用")this.stdNo = stdNo}override def printInfo(): Unit = {println(s"Student: $name $age $stdNo")}
}class Teacher extends Person7 {override def printInfo(): Unit = {println(s"Teacher")}
}

image-20230413205553026

6.5 多态(动态绑定)

与java的区别

scala中的属性和方法都是动态绑定,也就是说,new的是什么,就调用什么的属性和方法

java中的属性是静态绑定方法是动态绑定,也就是说,定义为父类,而new子类,调用的属性是父类的属性值,调用的方法是子类的方法

java多态

public class TestDynamicBind {public static void main(String[] args) {Worker worker = new Worker();System.out.println(worker.name);worker.hello();worker.hi();System.out.println("===================");// 多态:定义的是Person父类,但new的是Worder子类Person person = new Worker();// 静态绑定属性,所以打印的是person的属性System.out.println(person.name); // 动态绑定方法,所以打印的是worker的方法person.hello();    // 由于父类中没有hi方法,所以报错
//        person.hi();     // error}
}class Person {String name = "person";public void hello() {System.out.println("hello person");}
}class Worker extends Person {String name = "worker";public void hello() {System.out.println("hello worker");}public void hi() {System.out.println("hi worker");}
}

image-20230413211330954

scala多态

object Test08_DynamicBind {def main(args: Array[String]): Unit = {//同样定义的是父类,实例化的是子类val student: Person8 = new Student8//与JAVA不同,SCALA的属性也是动态绑定,所以是Student8的属性println(student.name)//调用的是是Student8的方法student.hello()}
}class Person8 {val name: String = "person"def hello(): Unit = {println("hello person")}
}class Student8 extends Person8 {override val name: String = "student"override def hello(): Unit = {println("hello student")}
}

image-20230413211402310

6.6 抽象类

基本语法

  1. 定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
  2. 定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
  3. 定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
package chapter06object Test09_AbstractClass {def main(args: Array[String]): Unit = {val student = new Student9student.eat()student.sleep()}
}// 定义抽象类
abstract class Person9{// 非抽象属性var name: String = "person"// 抽象属性var age: Int// 非抽象方法def eat(): Unit = {println("person eat")}// 抽象方法def sleep(): Unit
}// 定义具体的实现子类
class Student9 extends Person9 {// 实现抽象属性,必须var,override可有可无var age: Int = 18// 实现抽象方法,直接定义就行,override可有可无def sleep(): Unit = {println("student sleep")}// 重写非抽象属性,因为父类name是var修饰的,直接写就可以// 如果父类是val,则子类都不可以修改,直接继承父类的
//  override val name: String = "student"name = "student"//重写非抽象方法override def eat(): Unit = {super.eat()println("student eat")}
}

image-20230413211732374

6.6.1 匿名抽象类
object Test10_AnnoymousClass {def main(args: Array[String]): Unit = {val person: Person10 = new Person10 {//有无override都可override var name: String = "alice"override def eat(): Unit = println("person eat")}println(person.name)person.eat()}
}// 定义抽象类
abstract class Person10 {var name: Stringdef eat(): Unit
}

6.7 单例对象(伴生对象)

Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

基本语法

object Test11_Object {def main(args: Array[String]): Unit = {
//    val student = new Student11("alice", 18)
//    student.printInfo()// 实现构造方法私有化,也就是类.“静态”方法val student1 = Student11.newStudent("alice", 18)student1.printInfo()val student2 = Student11.apply("bob", 19)student2.printInfo()val student3 = Student11("bob", 19)student3.printInfo()}
}// 定义类,private表明主构造器私有化了
class Student11 private(val name: String, val age: Int){def printInfo(): Unit = {// 注意这里的Student11.school,也就是类名.属性println(s"student: name = ${name}, age = $age, school = ${Student11.school}")}
}// 伴生对象,可以访问伴生类的私有成员和方法
object Student11{//这个变量 就相当于java中的static修饰的静态变量val school: String = "atguigu"// 定义一个类的对象实例的创建方法,该方法也是静态方法def newStudent(name: String, age: Int): Student11 = new Student11(name, age)//如果参数名称是apply,则调用的时候可以省略类.apply(),直接写Student11("bob", 19)def apply(name: String, age: Int): Student11 = new Student11(name, age)
}
6.7.1 单例设计模式

也就是类只有一个,类中的属性先前就定义好了,写死了

object Test12_Singleton {def main(args: Array[String]): Unit = {val student1 = Student12.getInstance()student1.printInfo()val student2 = Student12.getInstance()student2.printInfo()// student1和student2的地址是相同的println(student1)println(student2)}
}class Student12 private(val name: String, val age: Int){def printInfo(): Unit = {println(s"student: name = ${name}, age = $age, school = ${Student11.school}")}
}// 饿汉式,无论Student12类是否存在,都会重新new
//object Student12 {
//  private val student: Student12 = new Student12("alice", 18)
//  def getInstance(): Student12 = student
//}// 懒汉式,如果单例类不存在,才会new
object Student12 {// 相当于定义了静态变量student,它是Student12类型的,只有一份private var student: Student12 = _def getInstance(): Student12 = {if (student == null){// 如果没有对象实例的话,就创建一个student = new Student12("alice", 18)}student}
}

6.8 特质(trait)

某个类,它的本质,就用它的父类来体现,是继承关系。比如student类的本质就用它的父类person来体现。

motivation

然而,像student类,他会有young person或是old person,这两个类的特性是不一样的,在java中就定义了接口,使能够保持它的特性

scala中没有接口的概念,采用特质 trait(特征)来代替。Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。 Scala 引入 trait 特征,第一可以替代 Java 的接口,第二个也是对单继承机制的一种补充(有点类似于多继承的味道,但实际上还是为单继承)

基本语法

没有父类:class 类名 extends 特质 1 with 特质 2 with 特质 3

有父类:class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3

package chapter06object Test13_Trait {def main(args: Array[String]): Unit = {val student: Student13 = new Student13student.sayHello()student.study()student.dating()student.play()}
}// 定义一个父类
class Person13 {val name: String = "person"var age: Int = 18def sayHello(): Unit = {println("hello from: " + name)}def increase(): Unit = {println("person increase")}
}// 定义一个特质
trait Young {// 声明抽象和非抽象属性var age: Intval name: String = "young"// 声明抽象和非抽象的方法def play(): Unit = {println(s"young people $name is playing")}def dating(): Unit
}class Student13 extends Person13 with Young {// Person13中有name=person,Yong中有name=young,需要重写冲突的属性override val name: String = "student"// 实现抽象方法def dating(): Unit = println(s"student $name is dating")def study(): Unit = println(s"student $name is studying")// 重写父类方法override def sayHello(): Unit = {super.sayHello()println(s"hello from: student $name")}
}

image-20230413222947704

6.8.1 多特质(mixin)动态混入
package chapter06object Test14_TraitMixin {def main(args: Array[String]): Unit = {val student = new Student14student.study()student.increase()student.play()student.increase()student.dating()student.increase()println("===========================")// 动态混入,也就是要什么特性就给什么特质val studentWithTalent = new Student14 with Talent {override def dancing(): Unit = println("student is good at dancing")override def singing(): Unit = println("student is good at singing")}studentWithTalent.sayHello()studentWithTalent.play()studentWithTalent.study()studentWithTalent.dating()studentWithTalent.dancing()studentWithTalent.singing()}
}// 再定义一个特质
trait Knowledge {var amount: Int = 0def increase(): Unit
}trait Talent {def singing(): Unitdef dancing(): Unit
}class Student14 extends Person13 with Young with Knowledge {// 重写冲突的属性override val name: String = "student"// 实现抽象方法def dating(): Unit = println(s"student $name is dating")def study(): Unit = println(s"student $name is studying")// 重写父类方法override def sayHello(): Unit = {super.sayHello()println(s"hello from: student $name")}// 实现特质中的抽象方法override def increase(): Unit = {amount += 1println(s"student $name knowledge increased: $amount")}
}
6.8.2 特质的叠加

image-20230414111208932

由于一个类可以混入(mixin)多个 trait,且 trait 中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。 冲突分为以下两种:

解决这类冲突问题,直接在类(Sub)中重写冲突方法。

image-20230414111305482

所谓的“钻石问题”,解决这类冲突问题,Scala 采用了特质叠加的策略。

package chapter06object Test15_TraitOverlying {def main(args: Array[String]): Unit = {// 钻石问题特征叠加val myFootBall = new MyFootBallprintln(myFootBall.describe())}
}
// 定义球类特征
trait Ball {def describe(): String = "ball"
}
// 定义颜色特征
trait ColorBall extends Ball {var color: String = "red"override def describe(): String = color + "-" + super.describe()
}// 定义种类特征
trait CategoryBall extends Ball {var category: String = "foot"override def describe(): String = category + "-" + super.describe()
}// 定义一个自定义球的类
class MyFootBall extends CategoryBall with ColorBall {override def describe(): String = "my ball is a " + super[CategoryBall].describe()
}
image-20230414111358864

image-20230414112339085

案例中的 super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质, 即,MyClass 中的 super 指代 Color,Color 中的 super 指代 Category,Category 中的 super 指代 Ball

6.8.3 指定super

除了上面的推断外,也可以指定使用哪个父类的方法

image-20230414112810278

6.8.4 特质和抽象类的区别
  1. 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
  2. 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数, 而特质不行(有无参构造)。
6.8.5 特质自身类型

自身类型可实现依赖注入的功能。

object Test16_TraitSelfType {def main(args: Array[String]): Unit = {val user = new RegisterUser("alice", "123456")user.insert()}
}// 用户类
class User(val name: String, val password: String)trait UserDao {// 相当于依赖注入的效果 _只是一个通配符 表示UserDao会用到User,但它们没有继承关系_: User =>// 向数据库插入数据def insert(): Unit = {// 使用this来调用Userprintln(s"insert into db: ${this.name}")}
}// 定义注册用户类
class RegisterUser(name: String, password: String) extends User(name, password) with UserDao

6.9 扩展

6.9.1 类型检查和转换
  • obj.isInstanceOf[T]:判断 obj 是不是 T 类型。

  • obj.asInstanceOf[T]:将 obj 强转成 T 类型。

  • classOf 获取对象的类名。

6.9.2 枚举类和应用类
// 定义枚举类对象
object WorkDay extends Enumeration {// 1,2表示键,也就是底层的存储val MONDAY = Value(1, "Monday")val TUESDAY = Value(2, "TuesDay")
}// 定义应用类对象
object TestApp extends App {println("app start")// type 起别名type MyString = Stringval a: MyString = "abc"println(a)
}// 2. 测试枚举类,直接引用就行
println(WorkDay.MONDAY)

7 集合

  1. Scala 的集合有三大类:序列 Seq集 Set映射 Map,所有的集合都扩展自 Iterable 特质。
  2. 对于几乎所有的集合类,Scala 都同时提供了可变不可变的版本,分别位于以下两 个包(scala.collection.immutable scala.collection.mutable)
  3. Scala 不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而 不会对原对象进行修改。类似于 java 中的 String 对象
  4. 可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似 于 java 中 StringBuilder 对象

建议

在操作集合的时候,不可变用符号可变用方法

7.1 不可变集合继承图

image-20230414131705294

7.2 可变集合继承图

image-20230414132345419

7.3 数组 相当于JAVA中的List

7.3.1 不可变数组

并不是说数组的值不可以修改,而是指向该数组的地址是不变的

创建数组

val arr1 = new Array[Int](10)
// 这里调用的是Array.apply()方法
val arr2 = Array(1,54,46,15,45)

数组遍历和访问

// 数组访问
println(arr(0))
// 3. 数组的遍历
// 1) 普通for循环 until前闭后开
for (i <- 0 until arr.length){println(arr(i))
}
for (i <- arr.indices) println(arr(i))
println("---------------------")
// 2) 直接遍历所有元素,增强for循环
for (elem <- arr2) println(elem)
println("---------------------")
// 3) 迭代器
val iter = arr2.iterator
while (iter.hasNext)println(iter.next())
println("---------------------")
// 4) 调用foreach方法
arr2.foreach( (elem: Int) => println(elem) )
arr.foreach( println )
println(arr2.mkString("--")) //打印结果为12--37--42--58--97

数组添加

// 4. 添加元素,在数组后面加用:+
val newArr = arr2.:+(73)
// arr2是不变的
println(arr2.mkString("--"))
println(newArr.mkString("--"))
// 在数组前面添加元素用+:
val newArr2 = newArr.+:(30)
println(newArr2.mkString("--"))
//所有的运算符也是函数,可以省略()
val newArr3 = newArr2 :+ 15
// +一定在数字那边
val newArr4 = 19 +: 29 +: newArr3 :+ 26 :+ 73
println(newArr4.mkString(", "))
7.3.2 可变数组

创建数组

val arr1: ArrayBuffer[Int] = new ArrayBuffer[Int]()
val arr2 = ArrayBuffer(23, 57, 92)
//直接使用println可以输出
println(arr2)

数组遍历和访问

// 遍历和访问同不可变数组
println(arr2(1))

添加元素

// 3. 添加元素
val newArr1 = arr1 :+ 15
// arr1依旧是不变的
println(arr1)
println(newArr1)
println(arr1 == newArr1)
// 对于可变数组而言,非常不推荐将返回的值再赋给另外一个变量=============
val newArr2 = arr1 += 19
// arr1 改变了
println(arr1)
println(newArr2)
// 是一个东西 结果为true
println(arr1 == newArr2)
// 如果newArr2改变了
newArr2 += 13
// arr1 也会改变
println(arr1)
// 在数组前面添加元素
77 +=: arr1
println(arr1)
println(newArr2)
// 在后面添加=======================可变数组推荐使用方法===============
arr1.append(36)
// 在前面添加
arr1.prepend(11, 76)
// 在索引为1的位置添加13和39
arr1.insert(1, 13, 59)
println(arr1)
// 在索引为2的位置,添加一个数组
arr1.insertAll(2, newArr1)
// 在前面添加一个数组
arr1.prependAll(newArr2)
// 在后面添加一个数组
arr1.appendAll(newArr2)

删除元素

// 4. 删除索引3的元素
arr1.remove(3)
println(arr1)
// 从索引0开始删除10个数
arr1.remove(0, 10)
println(arr1)
// 删除第一个值为13的数,如果13不在数组内,啥也不做
arr1 -= 13
7.3.3 可变数组和不可变数组的转换
// 5. 可变数组转换为不可变数组toArray
val arr: ArrayBuffer[Int] = ArrayBuffer(23, 56, 98)
val newArr: Array[Int] = arr.toArray
println(newArr.mkString(", "))
println(arr)
// 6. 不可变数组转换为可变数组toBuffer
val buffer: mutable.Buffer[Int] = newArr.toBuffer
println(buffer)
println(newArr)
7.3.4 多维数组
// 1. 创建二维数组
val array: Array[Array[Int]] = Array.ofDim[Int](2, 3)// 2. 访问元素
array(0)(2) = 19
array(1)(0) = 25
// 3.遍历二维数组
println(array.mkString(", "))
for (i <- 0 until array.length; j <- 0 until array(i).length){println(array(i)(j))
}
for (i <- array.indices; j <- array(i).indices){print(array(i)(j) + "\t")if (j == array(i).length - 1) println()
}
array.foreach(line => line.foreach(println))
// 简化写法
array.foreach(_.foreach(println))

7.4 列表 相当于JAVA中的LinkedList

7.4.1 不可变列表

创建list

// 1. 创建一个List,不能new,只能使用伴生对象的.apply方法
val list1 = List(23, 65, 87)
println(list1)
// List(73,32)
val list6 = 73 :: 32 :: Nil
// List(17,28,59,6 )
val list7 = 17 :: 28 :: 59 :: 16 :: Nil

访问和遍历list

// 2. 访问和遍历元素
println(list1(1))
//    无法修改元素的值 list1(1) = 12
list1.foreach(println)

添加元素

val list2 = 10 +: list1
val list3 = list1 :+ 23
println(list1)
println(list2)
println(list3)
println("==================")
// 调用特殊的方法::将51放在list的前面
val list4 = list2.::(51)
println(list4)
// 一般用Nil.::(元素)在创建新list
val list5 = Nil.::(13)
println(list5)
// List(73,32)
val list6 = 73 :: 32 :: Nil
// List(17,28,59,16)
val list7 = 17 :: 28 :: 59 :: 16 :: Nil
println(list7)

合并列表

// 4. 合并列表
// List(73,32)
val list6 = 73 :: 32 :: Nil
// List(17,28,59,16)
val list7 = 17 :: 28 :: 59 :: 16 :: Nil
val list8 = list6 :: list7
// list8=List(List(73,32),17,28,59,16)
println(list8)
// list9=List(73,32,17,28,59,16)
val list9 = list6 ::: list7
println(list9)
// 结果同上
val list10 = list6 ++ list7
println(list10)
7.4.2可变列表
// 1. 创建可变列表 
val list1: ListBuffer[Int] = new ListBuffer[Int]()
val list2 = ListBuffer(12, 53, 75)
println(list1)
println(list2)
println("==============")// 2. 添加元素
list1.append(15, 62)
list2.prepend(20)
// 在索引为1的位置加19,22
list1.insert(1, 19, 22)
println(list1)
println(list2)println("==============")
31 +=: 96 +=: list1 += 25 += 11
println(list1)
println("==============")
// 3. 合并list
val list3 = list1 ++ list2
// list1 list2并不更改
println(list1)
println(list2)println("==============")
// list2改变,因为有:是从右到左改变
list1 ++=: list2
println(list1)
println(list2)println("==============")// 4. 修改索引为3的元素
list2(3) = 30
// 修改索引为0的元素为89
list2.update(0, 89)
println(list2)// 5. 删除元素
list2.remove(2)
// 指定删除值为25的值
list2 -= 25
println(list2)

7.5 集合Set

7.5.1 不可变集合
// 1. 创建set,重复的数据会自动删除,且是乱序的
val set1 = Set(13, 23, 53, 12, 13, 23, 78)
println(set1)println("==================")// 2. 添加元素
val set2 = set1 + 129
println(set1)
println(set2)
println("==================")// 3. 合并set
val set3 = Set(19, 13, 23, 53, 67, 99)
val set4 = set2 ++ set3
println(set2)
println(set3)
println(set4)// 4. 删除元素
val set5 = set3 - 13
println(set3)
println(set5)
7.5.2 可变集合
// 1. 创建set,使用mutable.Set
val set1: mutable.Set[Int] = mutable.Set(13, 23, 53, 12, 13, 23, 78)
println(set1)
println("==================")// 2. 添加元素
val set2 = set1 + 11
// set1并没有改变
println(set1)
println(set2)
// set1改变了
set1 += 11
println(set1)
// 修改了set就返回true
val flag1 = set1.add(10)
println(flag1)
println(set1)
val flag2 = set1.add(10)
println(flag2)
println(set1)
println("==================")// 3. 删除元素
set1 -= 11
println(set1)
val flag3 = set1.remove(10)
println(flag3)
println(set1)
val flag4 = set1.remove(10)
println(flag4)
println(set1)
println("==================")// 4. 合并两个Set,set1改变set2不变
set1 ++= set2

7.6 Map

7.6.1 不可变Map
// 1. 创建map
val map1: Map[String, Int] = Map("a" -> 13, "b" -> 25, "hello" -> 3)
println(map1)
println(map1.getClass) //class scala.collection.immutable.Map$Map3
println("==========================")
// 2. 遍历元素
map1.foreach(println)
map1.foreach( (kv: (String, Int)) => println(kv) )
println("============================")
// 3. 取map中所有的key 或者 value
for (key <- map1.keys){println(s"$key ---> ${map1.get(key)}") //输出为a ---> Some(13)
}
// 4. 访问某一个key的value
println("a: " + map1.get("a").get) //获得具体的值map1.get("a").get)
// 获得key为a的值
println(map1("a"))
println("c: " + map1.get("c"))
// 如果c不存在,返回0
println("c: " + map1.getOrElse("c", 0))
7.6.2 可变Map
// 1. 创建map
val map1: mutable.Map[String, Int] = mutable.Map("a" -> 13, "b" -> 25, "hello" -> 3)
println(map1)
println(map1.getClass) // class scala.collection.mutable.HashMap
println("==========================")// 2. 添加元素
map1.put("c", 5)
map1.put("d", 9)
println(map1)
// 注意是使用(()),原因是若只加一个(),编译器会以为省略了一个(),中间的内容又是另外一个函数,也就是两个函数
map1 += (("e", 7))
println(map1)
println("====================")// 3. 删除元素
println(map1("c"))
map1.remove("c")
println(map1.getOrElse("c", 0))
map1 -= "d"
println(map1)
println("====================")// 4. 修改元素
map1.update("c", 5)
map1.update("e", 10)
println(map1)
println("====================")// 5. 合并两个Map,map2是不可变的
val map2: Map[String, Int] = Map("aaa" -> 11, "b" -> 29, "hello" -> 5)
// map1修改
map1 ++= map2
println(map1)
println(map2)
println("---------------------------")
val map3: Map[String, Int] = map2 ++ map1
println(map1)
println(map2)
println(map3)

7.7 元组

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。元组中最大只能有 22 个元素。

// 1. 创建元组
val tuple: (String, Int, Char, Boolean) = ("hello", 100, 'a', true)
println(tuple)// 2. 访问数据,从下标1开始
println(tuple._1)
println(tuple._2)
println(tuple._3)
println(tuple._4)println(tuple.productElement(1))println("====================")
// 3. 遍历元组数据
for (elem <- tuple.productIterator)println(elem)// 4. 嵌套元组
val mulTuple = (12, 0.3, "hello", (23, "scala"), 29)
println(mulTuple._4._2)

7.8 集合函数

7.8.1 常用函数
val list = List(1,3,5,7,2,89)
val set = Set(23,34,423,75)//    (1)获取集合长度,只有线性序列才有的属性
println(list.length)//    (2)获取集合大小 set不能获取长度
println(set.size)//    (3)循环遍历
for (elem <- list)println(elem)set.foreach(println)//    (4)迭代器
for (elem <- list.iterator) println(elem)println("====================")
//    (5)生成字符串
println(list)
println(set)
println(list.mkString("--"))//    (6)是否包含
println(list.contains(23))
println(set.contains(23))
7.8.2 衍生操作
val list1 = List(1,3,5,7,2,89)
val list2 = List(3,7,2,45,4,8,19)//    (1)获取集合的头1
println(list1.head)//    (2)获取集合的尾(不是头的就是尾)List(3, 5, 7, 2, 89)
println(list1.tail)//    (3)集合最后一个数据19
println(list2.last)//    (4)集合初始数据(不包含最后一个)List(3, 7, 2, 45, 4, 8)
println(list2.init)//    (5)反转List(89, 2, 7, 5, 3, 1)
println(list1.reverse)//    (6)取前(后)n个元素
println(list1.take(3))
println(list1.takeRight(4))//    (7)去掉前(后)n个元素
println(list1.drop(3))
println(list1.dropRight(4))println("=========================")
//    (8)并集
val union = list1.union(list2)
println("union: " + union)
println(list1 ::: list2)// 如果是set做并集,会去重
val set1 = Set(1,3,5,7,2,89)
val set2 = Set(3,7,2,45,4,8,19)val union2 = set1.union(set2)
println("union2: " + union2)
println(set1 ++ set2)
println("-----------------------")//    (9)交集
val intersection = list1.intersect(list2)
println("intersection: " + intersection)
println("-----------------------")//    (10)差集,存在一个集合中(list1),但不存在另外一个集合中(list2)
val diff1 = list1.diff(list2)
val diff2 = list2.diff(list1)
println("diff1: " + diff1)
println("diff2: " + diff2)
println("-----------------------")//    (11)拉链(a,b,c) (d,e,f,g) => ((a,d),(b,e),(c,f))
println("zip: " + list1.zip(list2))//List((1,3), (3,7), (5,2), (7,45), (2,4), (89,8))
println("zip: " + list2.zip(list1))//List((3,1), (7,3), (2,5), (45,7), (4,2), (8,89))
println("-----------------------")//    (12)滑窗,一定划过整个数组
// list1(1,3,5,7,2,89) => (1, 3, 5) (3, 5, 7) (5, 7, 2) (7, 2, 89)
for (elem <- list1.sliding(3))println(elem)
println("-----------------------")
// 窗口大小为4,每次滑动2个
for (elem <- list2.sliding(4, 2))println(elem)println("-----------------------")
for (elem <- list2.sliding(3, 3))println(elem)
7.8.3 数学操作
val list = List(5,1,8,2,-3,4)
val list2 = List(("a", 5), ("b", 1), ("c", 8), ("d", 2), ("e", -3), ("f", 4))//    (1)求和
var sum = 0
for (elem <- list){sum += elem
}
println(sum)
println(list.sum)//    (2)求乘积
println(list.product)//    (3)最大值
println(list.max)
// 指定怎样找到最大值,比如看第二个字段
println(list2.maxBy( (tuple: (String, Int)) => tuple._2 ))
println(list2.maxBy( _._2 ))//    (4)最小值
println(list.min)
println(list2.minBy(_._2))
println("========================")//    (5)排序
// 5.1 sorted
val sortedList = list.sorted
println(sortedList)// 从大到小逆序排序
println(list.sorted.reverse)
// 传入隐式参数
println(list.sorted(Ordering[Int].reverse))
println(list2.sorted)// 5.2 sortBy设置排序的方式
println(list2.sortBy(_._2))
println(list2.sortBy(_._2)(Ordering[Int].reverse))// 5.3 sortWith
println(list.sortWith( (a: Int, b: Int) => {a < b} ))
// 从小到大
println(list.sortWith( _ < _ ))
println(list.sortWith( _ > _))
7.8.4 map操作
val list = List(0,1,2,3,4,5,6,7,8,9)// 1. 过滤filter
// 选取偶数
val evenList = list.filter( (elem: Int) => {elem % 2 == 0} )
println(evenList)
// 选取奇数
println(list.filter( _ % 2 == 1 ))
println("=======================")// 2. 映射map
// 把集合中每个数乘2
println(list.map(_ * 2))
println(list.map( x => x * x))
println("=======================")// 3. 扁平化flatten
val nestedList: List[List[Int]] = List(List(1,2,3),List(4,5),List(6,7,8,9))
val flatList = nestedList(0) ::: nestedList(1) ::: nestedList(2)
println(flatList)val flatList2 = nestedList.flatten
println(flatList2)
println("=======================")// 4. 扁平映射flatMap
// 将一组字符串进行分词,并保存成单词的列表
val strings: List[String] = List("hello world", "hello scala", "hello java", "we study")
val splitList: List[Array[String]] = strings.map( _.split(" ") )    // 分词
val flattenList = splitList.flatten    // 打散扁平化
println(flattenList)val flatmapList = strings.flatMap(_.split(" "))
println(flatmapList)
println("========================")// 5. 分组groupBy
// 分成奇偶两组Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(0, 2, 4, 6, 8))
val groupMap: Map[Int, List[Int]] = list.groupBy( _ % 2)
// Map(奇数 -> List(1, 3, 5, 7, 9), 偶数 -> List(0, 2, 4, 6, 8))
val groupMap2: Map[String, List[Int]] = list.groupBy( data => if (data % 2 == 0) "偶数" else "奇数")println(groupMap)
println(groupMap2)// 给定一组词汇,按照单词的首字母进行分组groupBy
// Map(b -> List(bob), j -> List(japan), a -> List(america, alice), c -> List(china, canada, cary))
val wordList = List("china", "america", "alice", "canada", "cary", "bob", "japan")
println( wordList.groupBy( _.charAt(0) ) )
7.8.5 reduce操作

foldreduce的区别就是fold是具有初始值的,是以初始值为主的计算

// 1. reduce 示例为求和
println(list.reduce( _ + _ ))
// 从左往右算
println(list.reduceLeft(_ + _))
// 从右往左算
println(list.reduceRight(_ + _))
println("===========================")val list2 = List(3,4,5,8,10)
// 从左往右减
println(list2.reduce(_ - _))    // -24
println(list2.reduceLeft(_ - _))
// 底层代码是递归调用
println(list2.reduceRight(_ - _))    // 3 - (4 - (5 - (8 - 10))), 6println("===========================")
// 2. fold有初始值
println(list.fold(10)(_ + _))     // 10 + 1 + 2 + 3 + 4
println(list.foldLeft(10)(_ - _))    // 10 - 1 - 2 - 3 - 4
println(list2.foldRight(11)(_ - _))    // 3 - (4 - (5 - (8 - (10 - 11)))),  -5
7.8.6 map合并
val map1 = Map("a" -> 1, "b" -> 3, "c" -> 6)val map2 = mutable.Map("a" -> 6, "b" -> 2, "c" -> 9, "d" -> 3)//println(map1 ++ map2) 值就是map2// 以map2作为初始值,底层是递归调用的,所以map2应当为可变Map// 因为fold中两个类型必须一样,所以使用foldLeftval map3 = map1.foldLeft(map2)(// mergedMap表示结果,初始值为map2,kv表示的是map1中的元素(mergedMap, kv) => {val key = kv._1val value = kv._2println(kv._1+' '+kv._2)mergedMap(key) = mergedMap.getOrElse(key, 0) + valuemergedMap}
7.8.7 单词统计
 val stringList: List[String] = List("hello","hello world","hello scala","hello spark from scala","hello flink from scala")// 1. 对字符串进行切分,得到一个打散所有单词的列表
//    val wordList1: List[Array[String]] = stringList.map(_.split(" "))
//    val wordList2: List[String] = wordList1.flatten
//    println(wordList2)val wordList:List[String] = stringList.flatMap(_.split(" "))println(wordList)// 2. 相同的单词进行分组,groupBy传入的函数就是(word返回值为它自己)val groupMap: Map[String, List[String]] = wordList.groupBy(word => word)println(groupMap)// 3. 对分组之后的list取长度,得到每个单词的个数val countMap: Map[String, Int] = groupMap.map(kv => (kv._1, kv._2.length))println(countMap)// 4. 将map转换为list,并排序取前3val sortList: List[(String, Int)] = countMap.toList.sortWith( _._2 > _._2 ).take(3)println(sortList)

结果

List(hello, hello, world, hello, scala, hello, spark, from, scala, hello, flink, from, scala)
Map(world -> List(world), flink -> List(flink), spark -> List(spark), scala -> List(scala, scala, scala), from -> List(from, from), hello -> List(hello, hello, hello, hello, hello))
Map(world -> 1, flink -> 1, spark -> 1, scala -> 3, from -> 2, hello -> 5)
List((hello,5), (scala,3), (from,2))

7.8.8 复杂单词统计
// 1. 将字符串打散为单词,并结合对应的个数包装成二元组
val preCountList: List[(String, Int)] = tupleList.flatMap(tuple => {val strings: Array[String] = tuple._1.split(" ")strings.map( word => (word, tuple._2) )}
)
println(preCountList)
// 2. 对二元组按照单词进行分组
val preCountMap: Map[String, List[(String, Int)]] = preCountList.groupBy( _._1 )
println(preCountMap)// 3. 叠加每个单词预统计的个数值
val countMap: Map[String, Int] = preCountMap.mapValues(tupleList => tupleList.map(_._2).sum
)
println(countMap)// 4. 转换成list,排序取前3
val countList = countMap.toList.sortWith(_._2 > _._2).take(3)
println(countList)

7.9 队列

// 创建一个可变队列
val queue: mutable.Queue[String] = new mutable.Queue[String]()queue.enqueue("a", "b", "c")println(queue)
println(queue.dequeue())
println(queue)
println(queue.dequeue())
println(queue)queue.enqueue("d", "e")println(queue)
println(queue.dequeue())
println(queue)println("==========================")// 不可变队列
val queue2: Queue[String] = Queue("a", "b", "c")
val queue3 = queue2.enqueue("d")
println(queue2)
println(queue3)

7.10 并行集合

// 串行执行Vector(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...)
val result: immutable.IndexedSeq[Long] = (1 to 100).map(x => Thread.currentThread.getId
)
println(result)
// 并行执行ParVector(12, 12, 12, 12, 12, 12, 18, 18, 16, 16, 16, 19, 19...)
val result2: ParSeq[Long] = (1 to 100).par.map(x => Thread.currentThread.getId
)
println(result2)

8 模式匹配

代替了JAVA中的switch case

  1. 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句, 若此时没有 case _ 分支,那么会抛出 MatchError。
  2. 每个 case 中,不需要使用 break 语句,自动中断 case
  3. match case 语句可以匹配任何类型,而不只是字面量。
  4. => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以 使用{}括起来,也可以不括。

8.1 基本语法

// 1. 基本定义语法
val x: Int = 5
val y: String = x match {case 1 => "one"case 2 => "two"case 3 => "three"case _ => "other"
}
println(y)// 2. 示例:用模式匹配实现简单二元运算
val a = 25
val b = 13def matchDualOp(op: Char): Int = op match {case '+' => a + bcase '-' => a - bcase '*' => a * bcase '/' => a / bcase '%' => a % bcase _ => -1
}println(matchDualOp('+'))
println(matchDualOp('/'))
println(matchDualOp('\\'))println("=========================")

8.2 模式守卫

// 3. 模式守卫 也就是加了个判断
// 求一个整数的绝对值
def abs(num: Int): Int = {num match {case i if i >= 0 => icase i if i < 0 => -i}
}println(abs(67))
println(abs(0))
println(abs(-24))

8.3 匹配类型

package chapter08object Test02_MatchTypes {def main(args: Array[String]): Unit = {// 1. 匹配常量def describeConst(x: Any): String = x match {case 1 => "Int one"case "hello" => "String hello"case true => "Boolean true"case '+' => "Char +"case _ => ""}println(describeConst("hello"))println(describeConst('+'))println(describeConst(0.3))println("==================================")// 2. 匹配类型def describeType(x: Any): String = x match {case i: Int => "Int " + icase s: String => "String " + s// JVM中存在泛型擦除,也就是只能识别List,而不能识别其中的类型case list: List[String] => "List " + list// 可以识别Array及其内的元素类型case array: Array[Int] => "Array[Int] " + array.mkString(",")case a => "Something else: " + a}println(describeType(35))println(describeType("hello"))println(describeType(List("hi", "hello")))println(describeType(List(2, 23)))println(describeType(Array("hi", "hello")))println(describeType(Array(2, 23)))// 3. 匹配数组for (arr <- List(Array(0),Array(1, 0),Array(0, 1, 0),Array(1, 1, 0),Array(2, 3, 7, 15),Array("hello", 1, 30),)) {val result = arr match {case Array(0) => "0"case Array(1, 0) => "Array(1, 0)"case Array(x, y) => "Array: " + x + ", " + y    // 匹配两元素数组case Array(0, _*) => "以0开头的数组"case Array(x, 1, z) => "中间为1的三元素数组"case _ => "something else"}println(result)}println("=========================")// 4. 匹配列表// 方式一for (list <- List(List(0),List(1, 0),List(0, 0, 0),List(1, 1, 0),List(88),List("hello"))) {val result = list match {case List(0) => "0"case List(x, y) => "List(x, y): " + x + ", " + ycase List(0, _*) => "List(0, ...)"// list中有一个元素case List(a) => "List(a): " + acase _ => "something else"}println(result)}// 方式二val list1 = List(1, 2, 5, 7, 24)val list = List(24)list1 match {//first: 1, second: 2, rest: List(5, 7, 24)case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest")case _ => println("something else")}println("===========================")// 5. 匹配元组for (tuple <- List((0, 1),(0, 0),(0, 1, 0),(0, 1, 1),(1, 23, 56),("hello", true, 0.5))){val result = tuple match {case (a, b) => "" + a + ", " + bcase (0, _) => "(0, _)"case (a, 1, _) => "(a, 1, _) " + acase (x, y, z) => "(x, y, z) " + x + " " + y + " " + zcase _ => "something else"}println(result)}}
}

8.4 应用时匹配

package chapter08object Test03_MatchTupleExtend {def main(args: Array[String]): Unit = {// 1. 在变量声明时匹配val (x, y) = (10, "hello")println(s"x: $x, y: $y")val List(first, second, _*) = List(23, 15, 9, 78)println(s"first: $first, second: $second")val fir :: sec :: rest = List(23, 15 , 9, 78)println(s"first: $fir, second: $sec, rest: $rest")println("=====================")// 2. for推导式中进行模式匹配val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13))// 2.1 原本的遍历方式for (elem <- list){println(elem._1 + " " + elem._2)}// 2.2 将List的元素直接定义为元组,对变量赋值for ((word, count) <- list ){println(word + ": " + count)}println("-----------------------")// 2.3 可以不考虑某个位置的变量,只遍历key或者valuefor ((word, _) <- list)println(word)println("-----------------------")// 2.4 可以指定某个位置的值必须是多少for (("a", count) <- list){println(count)}}
}

8.5 匹配对象

package chapter08object Test04_MatchObject {def main(args: Array[String]): Unit = {val student = new Student("alice", 19)// 使用伴生对象针对对象实例的内容进行匹配val result = student match {case Student("alice", 18) => "Alice, 18"case _ => "Else"}println(result)}
}// 定义类
class Student(val name: String, val age: Int)// 定义伴生对象
object Student {def apply(name: String, age: Int): Student = new Student(name, age)// 必须实现一个unapply方法,用来对对象属性进行拆解 Option[(String, Int)]def unapply(student: Student): Option[(String, Int)] = {if (student == null){None} else {Some((student.name, student.age))}}
}

8.6 样例类

object Test05_MatchCaseClass {def main(args: Array[String]): Unit = {val student = Student1("alice", 18)// 针对对象实例的内容进行匹配val result = student match {case Student1("alice", 18) => "Alice, 18"case _ => "Else"}println(result)}
}// 定义样例类
case class Student1(name: String, age: Int)

8.7 偏函数

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如 该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式 匹配实现的。

image-20230414225150699

package chapter08object Test06_PartialFunction {def main(args: Array[String]): Unit = {val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13))// 1. map转换,实现key不变,value2倍val newList = list.map( tuple => (tuple._1, tuple._2 * 2) )// 2. 用模式匹配对元组元素赋值,实现功能val newList2 = list.map(tuple => {tuple match {case (word, count) => (word, count * 2)}})// 3. 省略lambda表达式的写法,进行简化val newList3 = list.map {case (word, count) => (word, count * 2)}println(newList)println(newList2)println(newList3)// 偏函数的应用,求绝对值// 对输入数据分为不同的情形:正、负、0val positiveAbs: PartialFunction[Int, Int] = {case x if x > 0 => x}val negativeAbs: PartialFunction[Int, Int] = {case x if x < 0 => -x}val zeroAbs: PartialFunction[Int, Int] = {case 0 => 0}def abs(x: Int): Int = (positiveAbs orElse negativeAbs orElse zeroAbs) (x)println(abs(-67))println(abs(35))println(abs(0))}
}

9 异常

需要注意以下几点:

  1. Scala 没有“checked(编译期)”异常,即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
  2. 所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing
def main(args: Array[String]): Unit = {try{val n = 10 / 0} catch {case e: ArithmeticException => {println("发生算术异常")}case e: Exception => {println("发生一般异常")}} finally {println("处理结束")}
}
def test():Nothing = {throw new Exception("不对")
}

10 隐式转换

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译

10.1 隐式函数和隐式类

def main(args: Array[String]): Unit = {val new12 = new MyRichInt(12)println(new12.myMax(15))// 1. 隐式函数,将int转换成MyRichInt,需要写在调用代码的前面implicit def convert(num: Int): MyRichInt = new MyRichInt(num)println(12.myMax(15))println("============================")// 2. 隐式类,必须放在object或者其他类的内部implicit class MyRichInt2(val self: Int) {// 自定义比较大小的方法def myMax2(n: Int): Int = if ( n < self ) self else ndef myMin2(n: Int): Int = if ( n < self ) n else self}println(12.myMin2(15))println("============================")
}
// 自定义类
class MyRichInt(val self: Int) {// 自定义比较大小的方法def myMax(n: Int): Int = if ( n < self ) self else ndef myMin(n: Int): Int = if ( n < self ) n else self
}

10.2 隐式参数

就是可以将参数的默认值定义在函数外部

// 3. 隐式参数,在同一作用范围内,相同类型的隐式参数只能有一个
implicit val str: String = "alice"
//    implicit val str2: String = "alice2"
implicit val num: Int = 18// 隐式参数底层使用了柯里化,调用的时候可以不用传参数,使用上面定义的隐式参数
def sayHello()(implicit name: String): Unit = {println("hello, " + name)
}
def sayHi(implicit name: String = "atguigu"): Unit = {println("hi, " + name)
}
// 调用可以加(),也可以不加
sayHello()
// 隐式参数会覆盖参数的默认值,所以输出的是hi atguigu
sayHi// 简便写法implicitly
def hiAge(): Unit = {// 指明调用Int的隐式参数println("hi, " + implicitly[Int])
}
hiAge()

11 泛型

11.1 协变和逆变

image-20230415103022271

object Test03_Generics {def main(args: Array[String]): Unit = {// 1. 协变和逆变val child: Parent = new Child// 协变,Child是Parent的子类,如果不使用协变,就无法定义//val childList: MyCollection[Parent] = new MyCollection[Child]// 逆变,Child是SubChild的子类,如果不使用逆变,就无法定义val childList: MyCollection[SubChild] = new MyCollection[Child]}
}// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}// 定义带泛型的集合类型,使用逆变
class MyCollection[-E] {}

11.2 泛型上下限

// 2. 上下限,传入的泛型A只能是Child及其子类
def test[A <: Child](a: A): Unit = {println(a.getClass.getName)
}
// 下面会报错,因为Parent不是Child及其子类
// test[Parent](new Child)
test[SubChild](new SubChild)
test[Child](new SubChild)
// 下面会报错,因为不能把父类(Child)的对象赋给子类(SubChild)
// test[SubChild](new Child)
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}

11.3 上下文限定

相当于内部包含一个隐式参数B[A]

def f[A : B](a: A) = println(a) 
//等同于 def f[A](a:A)(implicit arg:B[A])=println(a)

说明

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]] 获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

def main(args: Array[String]): Unit = {implicit val x = 1val y = implicitly[Int]// 下面会报错, 因为没有隐式的Double参数// val z = implicitly[Double]println(y)// 使用上下文限定,也就等同于下面的代码,相当于有一个隐式的参数,参数类型为Ordering[A]def f[A: Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)// def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)// 3<4 结果为-1println(f[Int](3,4))
}

9 异常

需要注意以下几点:

  1. Scala 没有“checked(编译期)”异常,即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
  2. 所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing
def main(args: Array[String]): Unit = {try{val n = 10 / 0} catch {case e: ArithmeticException => {println("发生算术异常")}case e: Exception => {println("发生一般异常")}} finally {println("处理结束")}
}
def test():Nothing = {throw new Exception("不对")
}

10 隐式转换

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译

10.1 隐式函数和隐式类

def main(args: Array[String]): Unit = {val new12 = new MyRichInt(12)println(new12.myMax(15))// 1. 隐式函数,将int转换成MyRichInt,需要写在调用代码的前面implicit def convert(num: Int): MyRichInt = new MyRichInt(num)println(12.myMax(15))println("============================")// 2. 隐式类,必须放在object或者其他类的内部implicit class MyRichInt2(val self: Int) {// 自定义比较大小的方法def myMax2(n: Int): Int = if ( n < self ) self else ndef myMin2(n: Int): Int = if ( n < self ) n else self}println(12.myMin2(15))println("============================")
}
// 自定义类
class MyRichInt(val self: Int) {// 自定义比较大小的方法def myMax(n: Int): Int = if ( n < self ) self else ndef myMin(n: Int): Int = if ( n < self ) n else self
}

10.2 隐式参数

就是可以将参数的默认值定义在函数外部

// 3. 隐式参数,在同一作用范围内,相同类型的隐式参数只能有一个
implicit val str: String = "alice"
//    implicit val str2: String = "alice2"
implicit val num: Int = 18// 隐式参数底层使用了柯里化,调用的时候可以不用传参数,使用上面定义的隐式参数
def sayHello()(implicit name: String): Unit = {println("hello, " + name)
}
def sayHi(implicit name: String = "atguigu"): Unit = {println("hi, " + name)
}
// 调用可以加(),也可以不加
sayHello()
// 隐式参数会覆盖参数的默认值,所以输出的是hi atguigu
sayHi// 简便写法implicitly
def hiAge(): Unit = {// 指明调用Int的隐式参数println("hi, " + implicitly[Int])
}
hiAge()

11 泛型

11.1 协变和逆变

[外链图片转存中…(img-9RgQjphE-1696143222645)]

object Test03_Generics {def main(args: Array[String]): Unit = {// 1. 协变和逆变val child: Parent = new Child// 协变,Child是Parent的子类,如果不使用协变,就无法定义//val childList: MyCollection[Parent] = new MyCollection[Child]// 逆变,Child是SubChild的子类,如果不使用逆变,就无法定义val childList: MyCollection[SubChild] = new MyCollection[Child]}
}// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}// 定义带泛型的集合类型,使用逆变
class MyCollection[-E] {}

11.2 泛型上下限

// 2. 上下限,传入的泛型A只能是Child及其子类
def test[A <: Child](a: A): Unit = {println(a.getClass.getName)
}
// 下面会报错,因为Parent不是Child及其子类
// test[Parent](new Child)
test[SubChild](new SubChild)
test[Child](new SubChild)
// 下面会报错,因为不能把父类(Child)的对象赋给子类(SubChild)
// test[SubChild](new Child)
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}

11.3 上下文限定

相当于内部包含一个隐式参数B[A]

def f[A : B](a: A) = println(a) 
//等同于 def f[A](a:A)(implicit arg:B[A])=println(a)

说明

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]] 获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

def main(args: Array[String]): Unit = {implicit val x = 1val y = implicitly[Int]// 下面会报错, 因为没有隐式的Double参数// val z = implicitly[Double]println(y)// 使用上下文限定,也就等同于下面的代码,相当于有一个隐式的参数,参数类型为Ordering[A]def f[A: Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)// def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)// 3<4 结果为-1println(f[Int](3,4))
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/146228.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

医疗图像分割指标

医疗图像其中两种图像格式&#xff1a;MRI&#xff08;Magnetic Resonance Imaging&#xff0c;磁共振成像&#xff09;、CT&#xff08;Computed Tomography&#xff0c;计算机断层&#xff09;&#xff0c;常存成 .nii.gz 格式。都是 3D 的 H W L H \times W \times L HWL…

【算法分析与设计】贪心算法(下)

目录 一、单源最短路径1.1 算法基本思想1.2 算法设计思想1.3 算法的正确性和计算复杂性1.4 归纳证明思路1.5 归纳步骤证明 二、最小生成树2.1 最小生成树性质2.1.1 生成树的性质2.1.2 生成树性质的应用 2.2 Prim算法2.2.1 正确性证明2.2.2 归纳基础2.2.3 归纳步骤2.3 Kruskal算…

重新认识mysql

title: “重新认识mysql” createTime: 2022-03-06T15:52:4108:00 updateTime: 2022-03-06T15:52:4108:00 draft: false author: “ggball” tags: [“mysql”] categories: [“db”] description: “” 文章目录 title: "重新认识mysql" createTime: 2022-03-06T15:…

Ghostscript 在 Linux 和 Windows 系统的应用与问题解决

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

windows WSL配置cuda,pytorch和jupyter notebook

机器配置 GPU: NVIDIA Quadro K2000 与 NVIDIA 驱动程序捆绑的CUDA版本 但按照维基百科的描述&#xff0c;我的GPU对应的compute capability3.0&#xff0c;允许安装的CUDA最高只支持10.2&#xff0c;如下所示。 为什么本地会显示11.4呢&#xff1f;对此&#xff0c;GPT是这…

QSS之QScrollArea

QScrollArea在实际的开发过程中经常使用&#xff0c;主要是有些界面一屏显示不下&#xff0c;所以得用QScorllArea带滚动条拖动显示剩余的界面。默认的QScrollArea滚动条不满设计的风格&#xff0c;因此我们必须设置自已的滚动条风格&#xff0c;QScrollBar分为水平horizontal和…

ORACLE Redo Log Buffer 重做日志缓冲区机制的设计

最近和朋友包括一些国产数据库的研发人员交流&#xff0c;很多程序员认为 Oracle 已经过时&#xff0c;开源数据库或者他们研发的国产数据库才代表数据库发展的未来。甚至在很多交流会议上拿出自家产品的某一个功能点和 Oracle 对比就觉得已经遥遥领先。 实际上数据库系统的发展…

zookeeper mac安装

目录 1.下载zookeeper安装包 2.解压安装包 3.修改配置文件 4.启动服务端 5.启动客户端 这边工作中用到了zookeeper组件&#xff0c;但自己独立安装弄的不太多&#xff0c;这边本机mac装一个做测试使用 以下是安装记录&#xff0c;可以作为参考 从以下链接zookeeper版本列…

windows系统利用powershell查看系统支持那些Windows功能选项

在PowerShell中&#xff0c;我们可以使用Get-WindowsOptionalFeature cmdlet命令来查看Windows功能选项。 打开PowerShell 输入以下命令&#xff1a;将结果输出到1.log Get-WindowsOptionalFeature -Online >1.log 可以看到在指定路径下看到生成了文件 打开查看内容&…

手机号码格式校验:@Phone(自定义参数校验注解)

需求 新增接口 和 修改接口 中&#xff0c;手机号码的格式校验是普遍需要的。 在每个手机号码字段上添加正则表达式校验注解来实现校验&#xff0c;重复书写&#xff0c;容易出错&#xff1b;在不同的手机号码字段上&#xff0c;可能使用了不同的校验规则&#xff0c;无法有效…

网络协议--链路层

2.1 引言 从图1-4中可以看出&#xff0c;在TCP/IP协议族中&#xff0c;链路层主要有三个目的&#xff1a; &#xff08;1&#xff09;为IP模块发送和接收IP数据报&#xff1b; &#xff08;2&#xff09;为ARP模块发送ARP请求和接收ARP应答&#xff1b; &#xff08;3&#xf…

【51单片机编写占空比按秒渐亮与渐暗】2023-10-2

昨天刚在W10上安装CH340驱动&#xff0c;又下载到板子上LCD1602定时器时钟程序&#xff0c;为了调试&#xff0c;调用了一个LED观察控制蜂鸣器按秒响的变量&#xff0c;几经调试才发觉该开发板用的是有源蜂鸣器&#xff0c;不用IO取反操作&#xff0c;直接控制IO的高低电平即可…

在亚马逊云科技Amazon SageMaker上部署构建聊天机器人的开源大语言模型

开源大型语言模型&#xff08;LLM&#xff09;已经变得流行起来&#xff0c;研究人员、开发人员和组织都可以使用这些模型来促进创新和实验。这促进了开源社区开展合作&#xff0c;从而为LLM的开发和改进做出贡献。开源LLM提供了模型架构、训练过程和训练数据的透明度&#xff…

讲讲项目里的仪表盘编辑器(二)

应用场景 正常来说&#xff0c;编辑器应用场景应该包括&#xff1a; 编辑器-预览 编辑器 最终运行时 怎么去设计 上一篇推文&#xff0c;我们已经大概了解了编辑器场景。接下来&#xff0c;我们来看预览时的设计 编辑器-预览 点击预览按钮&#xff0c;执行以…

<C++> 哈希表模拟实现STL_unordered_set/map

哈希表模板参数的控制 首先需要明确的是&#xff0c;unordered_set是K模型的容器&#xff0c;而unordered_map是KV模型的容器。 要想只用一份哈希表代码同时封装出K模型和KV模型的容器&#xff0c;我们必定要对哈希表的模板参数进行控制。 为了与原哈希表的模板参数进行区分…

百度资源搜索平台出现:You do not have the proper credential to access this page.怎么办?

Forbidden site not allowed You do not have the proper credential to access this page. If you think this is a server error, please contact the webmaster. 如果你的百度资源平台&#xff0c;点进去出现这个提示&#xff0c;说明您的网站已经被百度清退了。如果你的网站…

把握现在,热爱生活

博客主页&#xff1a;https://tomcat.blog.csdn.net 博主昵称&#xff1a;农民工老王 主要领域&#xff1a;Java、Linux、K8S 期待大家的关注&#x1f496;点赞&#x1f44d;收藏⭐留言&#x1f4ac; 目录 厨艺房价琐事计划随想 今年的中秋国庆假期放8天&#xff0c;比春节假期…

IIS管理器无法打开。启动后,在任务栏中有,但是窗口不见了

找到IIS管理器启动程序的所在位置 并在cmd命令行中调用 inetmgr.exe /reset 进行重启 先查看IIS管理器属性&#xff0c;找到其位置 管理员模式打开cmd命令行&#xff0c;并切换到上面的文件夹下运行Inetmgr.exe /reset 运行完成后可以重新看到IIS窗口 原因&#xff1a;由于某…

安防监控/视频汇聚平台EasyCVR云端录像不展示是什么原因?该如何解决?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

在Windows11家庭中文版中启用Copilot(预览版)

1、下载ViveTool-vx.x.x.zip 2、解压下载的压缩包ViveTool-vx.x.x.zip 3、复制ViveTool文件夹的路径 4、按下wins&#xff0c;打开搜索 5、输入cmd&#xff0c;并选择“以管理员身份运行” 6、在cmd中输入以下命令&#xff0c;进入ViveTool文件夹&#xff1a; cd ViveTool…