scala|Scala

Scala scala 是一门以 jvm 为运行环境的静态类型编程语言,具备面向对象 及 函数式编程的特性
六大特征 :

  • Java和scala可以混编
    • 可以和Java(相对)无缝衔接,注意运法差异,一般不推荐使用 var推荐使用 val
  • 类型推测(自动推测类型)
    • 根据声明值的类型推断出数据的类型,在Scala中,没有基本数据类型
  • 并发和分布式(Actor)
    • 线程,实现了自己的并发,拥有自己的并发操作组件Actor
  • 特质,特征(类似java中interfaces 和 abstract结合)
    • 可以有方法的实现,可以有普通的变量,可以实现多继承
  • 模式匹配(类似java中的switch…case)
    • 配置值是否相等,JDK8后可以匹配字符串
  • 高阶函数
    • Java中一般称为method
Scala 应用场景
  • kafka:分布式消息队列,内部代码经常用来处理并发的问题,用scala可以大大简化其代码
  • spark:方便处理多线程场景,另外spark主要用作内存计算,经常要用来实现复杂的算法。利用scala这种函数式编程语言可以大大简化代码
Scala基础 标识符
  • 区分大小写
    • Scala是大小写敏感的
  • 类名
    • 对于所有的类名的第一个字母要大写。
    • 驼峰原则
  • 方法名称
    • 所有的方法名称的第一个字母用小写。
    • 驼峰原则
  • 程序文件名
    • 程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。
    • 保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加".scala"为文件 扩展名。
  • 关键字
    • 避免使用关键字–>后面加01驼峰原则
数据类型
Scala的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类 型调用方法的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMi3rgwa-1658889492338)(C:\Users\58266\AppData\Roaming\Typora\typora-user-images\1657870236960.png)]
变量与常量
  • 变量:在程序运行过程中其值可能发生改变的量叫做变量。
  • 常量:在程序运行过程中其值不会发生变化的量叫做常量。
  • 在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是
  • 通过变量或常量的初始值推断出来的。如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
/** * 定义变量和常量 * 变量 :用 var 定义 ,可修改 * 常量 :用 val 定义,不可修改 */ var name = "zhangsan" println(name) name ="lisi" println(name) val gender = "m" //gender = "w" //错误,不能给常量再赋值

运算符
一个运算符是一个符号,用于告诉编译器来执行指定的数学运算和逻辑运算。
  • scala减去了++ 与 –
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8vOj7a4W-1658889492339)(C:\Users\58266\AppData\Roaming\Typora\typora-user-images\1657870937311.png)]
逻辑语句
  • Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。
  • 同 java 一样的语法结构,单,双,多分支这样三种顺序控制结构。
循环语句
循环语句允许我们多次执行一个语句或语句组
  • 区间获取
    • /** * to和until * 例: * 1 to 10 返回1到10的Range数组,包含10 * 1 until 10 返回1到10 Range数组 ,不包含10 */ (1 to 10).foreach(print)//打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (1 to 10).foreach(print)//与上面等价,打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10println(1 to (10 ,2))//步长为2,从1开始打印 ,1,3,5,7,9 println(1.to(10, 2)) println(1 until 10 ) //不包含最后一个数,打印 1,2,3,4,5,6,7,8,9 println(1.until(10))//与上面等价 println(1 until (10 ,3 ))//步长为2,从1开始打印,打印1,4,7

    • //借助数组的区间 类似于 until Array.range(10, 20) Array.range(10, 20, 2)

  • 普通for循环
    • /** * Range 可以是一个数字区间表示 i to j ,或者 i until j。左箭头 <- 用于为变量 x 赋值。 */ for( var x <- Range ){ statement(s); }

  • 多层for循环
    • //可以分号隔开,写入多个list赋值的变量,构成多层for循环 //scala中 不能写count++ count-- 只能写count+ var count = 0; for(i <- 1 to 10; j <- 1 until 10){ println("i="+ i +", j="+j) count += 1 } println(count); //例子: 打印小九九 for(i <- 1 until 10 ; j <- 1 until 10){ if(i>=j){ print(i +" * " + j + " = "+ i*j+" ") } if(i==j ){ println() } }

  • 判断for循环
    • //可以在for循环中加入条件判断 for(i<- 1 to 10 ; if (i%2) == 0 ; if (i == 4) ){ println(i) }

  • for循环与yidld
    • /* yield 关键字返回一个集合(`for {子句} yield {变量或表达式}`,原来的集合不会被 改变,只会通过你的for/yield构建出一个新的集合。) 将 for 循环的返回值作为一个变量存储 var retVal = for{ var x <- List if condition1; if condition2... }yield x */ object Test { def main(args: Array[String]) { var a = 0; val numList = List(1,2,3,4,5,6,7,8,9,10); // for 循环 var retVal = for{ a <- numList if a != 3; if a < 8 }yield a // 输出返回值 for( a <- retVal){ println( "Value of a: " + a ); } } }

  • while循环(两种方式), while(){},do {}while()
    • //将for中的符合条件的元素通过yield关键字返回成一个集合 val list = for(i <- 1 to 10 ; if(i > 5 )) yield i for( w <- list ){ println(w) } /** * while 循环 */ var index = 0 while(index < 100 ){ println("第"+index+"次while 循环") index += 1 } index = 0 do{ index +=1 println("第"+index+"次do while 循环") }while(index <100 )

方法与函数
方法的定义
Scala 使用 def 关键字告诉编译器这是一个方法。
  • 返回值类型与返回值
        • 在参数后面加一个冒号和类型来显式地指定返回类型。
        • 方法可以写返回值的类型也可以不写,会自动推断,但是如果有显式的Return有时候不能省略,必须写,
        • Scala 中函数有返回值时,可以写 return ,也可以不写 return ,不写 return 时会把函数中最后一行当做结果返回。
        • 如果去掉方法体前面的等号,那么这个方法返回类型必定是 Unit 的。scala可以把任意类型转
          换为 Unit 。
          • 假设,函数里面的逻辑最后返回了一个 string ,那么这个返回值会被转换成 Unit ,原本逻辑的值会被丢弃
      • def functionName ([参数列表]) : [return type] = { function body return [expr] } def method1 (a: Int , b: Int ) : Unit = { println(a+b) } method1(1,1) def method2 (a : Int , b : Int) = a+b println(method2(1,2)) def method3(s: String) { return s } println(method3("m3"))





函数的定义
  • Scala的函数是基于Function家族,0-22,一共23个Function Trait可以被使用,数字代表了Funtcion的入参个数
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y8p7wBCK-1658889492340)(C:\Users\58266\AppData\Roaming\Typora\typora-user-images\1657872025749.png)]
  • Java里只有方法都能适应一切需求,那scala又提出函数的概念肯定有意义。
    • 函数可以直接赋值给变量,可以让函数很方便的传递
    • 闭包(closure),可以把灵活操作代码块,从而引申出其他灵活的语法
  • var f1 = (s: String) => { println("hi1 " + s); } var f2 = (s: String) => { println("hi2 " + s); }

方法与函数
  • 对于java来说,方法和函数是等价的,或者
    说没有函数这个概念。而对于scala,这两者似乎有一个较为明确的边界
  • Scala有方法与函数,语义上有很小的区别
  • Scala 方法是类的一部分,而函数是一个对象 对象的引用可以赋值给一个变量。换句话来说在类中定义的函数即是方法
    • Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
    • Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
  • Scala 中使用 val 语句可以定义函数,def 语句定义方法。
    • def m(x: Int) = x + 3val f = (x: Int) => x + 3

  • 方法与函数的区别:
      • 当函数定义在类中,就表示方法,其他的都称为函数
      • 函数可以作为一个参数传入到方法中,而方法就不行
        • 在Scala中无法直接操作方法,如果要操作方法,必须先将其转换成函数,
        • 通常情况下,编译器会自动将方法转换成函数
        • val f = m _ //表示将m 方法转换为函数
        • def msum(nums: Array[Int]) = { var sum = 0; for (num <- nums) { sum += num; } sum } var fsum = msum _ println(fsum(Array[Int](1, 2, 3, 4, 5)));


      - `在需要函数的地方,如果传递一个方法,会自动进行ETA展开(把方法转换为函数)`?```scala def main(args: Array[String]): Unit = { method02(method01, "libai") } def method01(s: String) = { println("hi4 " + s); } def method02(fun: String => Unit, str: String) = { fun(str); }

      • 函数必须要有参数列表,而方法可以没有参数列表
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m0G6I3W9-1658889492340)(资源/1657891719536.png)]
      • 如果我们直接把一个方法赋值给变量会报错,(Missing arguments for method
        method01(Int,Int))
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMhC4o0c-1658889492341)(资源/1657891733346.png)]

闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
  • 闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数
  • //匿名的函数,函数体内有一个变量 i,它作为函数的一个参数。 val multiplier = (i:Int) => i * 10//multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数 //multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量 var factor = 3 val multiplier = (i:Int) => i * factor//函数变量 multiplier 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。 object Test { def main(args: Array[String]) { println( "muliplier(1) value = "https://www.it610.com/article/+ multiplier(1) ) println("muliplier(2) value = "https://www.it610.com/article/+ multiplier(2) ) } var factor = 3 val multiplier = (i:Int) => i * factor }

字符串
  • 在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
  • 在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
  • 在 Scala 中,可以使用 String Builder 类创建一个可以修改的字符串。
//创建字符串 var str1 = "Hello World!"; var str2:String = "Hello World!"; //创建可变字符串 val buf = new StringBuilder; buf += 'a' buf ++= "bcdef" println( "buf is : " + buf.toString );

数组
  • Scala 语言中提供的数组是用来存储固定大小的同类型元素,数组对于每一门编辑应语言来说都是重要的数据结构之一。
  • 数组的第一个元素索引为0,最后一个元素的索引为元素总数减1。
  • 创建数组的两种方式:
    • /** * 创建数组两种方式: * 1.new Array[String](3) * 2.直接Array */ //创建类型为Int 长度为3的数组 val arr1 = new Array[Int](3) arr1(0) = 100 arr1(1) = 200 arr1(2) = 300 //创建String 类型的数组,直接赋值 val arr2 = Array[String]("s100","s200","s300")

  • 遍历的两种方式
    • /** * 遍历两种方式 */ for(i <- arr1){ println(i) } arr1.foreach(i => { println(i) }) for(s <- arr2){ println(s) } arr2.foreach { x => println(x) } //最简单写法 arr2.foreach(println)

  • 创建二维数组
    • /** * 创建二维数组和遍历 */ val arr3 = new Array[Array[String]](3) arr3(0)=Array("1","2","3") arr3(1)=Array("4","5","6")arr3(2)=Array("7","8","9") for(i <- 0 until arr3.length){ for(j <- 0 until arr3(i).length){ print(arr3(i)(j)+" ") } println() }

  • 可变长度数组
    • //需要引入相应包,mutable就是可变,immutable就是不可变 import scala.collection.mutable.ArrayBuffer val arr = ArrayBuffer[String]("a","b","c") arr.append("hello","scala")//添加多个元素 arr.+=("end")//在最后追加元素 arr.+=:("start")//在开头添加元素 arr.foreach(println)

  • 数组复杂应用
    • //合并数组 var c = Array(1,2,3) var b = Array(4,5,6) b++c //合并数组 并将右边的类型作为最终结果返回.最后c为LinkList类型 val a = List(1,2) val b = scala.collection.mutable.LinkedList(3,4) val c = a ++: b //在数组前添加一个元素,数组在哪冒号在哪 val k = 0 val a = List(1,2) val c = k +: a // c中的内容是 (0,1,2) val d = a :+ k // d中的内容是 (1,2,0) //对数组中所有的元素进行相同的操作 val a = List(1,2,3,4) val c = (10 /: a)(_+_) // 1+2+3+4+10 val d = (10 /: a)(_*_) // 1*2*3*4*10 println("c:"+c) // c:20 println("d:"+d) // d:240 val a = List(1,2,3,4) val b = new StringBuilder("678") val c = a.addString(b) // c中的内容是 1234 val d = a.addString(b,",") // 连接字符串时每个元素按分隔符隔开 val e = a.addString(b,"shou",",","wei") // 在首尾各加一个字符串,并指定sep分 隔符 //取出指定索引处得元素 arr.apply(index) //判断两个对象是否可以进行比较 arr.canEqual() //创建一个副本,不是引用,是深拷贝 val chars = Array('a','b','c') val newchars = chars.clone()

类和对象
构造器
  • 每个类都有一个主要的构造器,这个构造器不是单独声明的构造函数,而是和类定义交织在一起。
  • 当你阅读一个Scala类时,你需要将它们分开理解,一个是类的定义,一个是构造函数的定义。
  • 除了主构造器之外,类还可以有任意多的辅助构造器(auxiliary constructor)。辅助构造器的名称为this
  • 每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始
属性
  • 类的所有属性默认都会被私有化,但是会生成getter和setter方法
  • 如果属性用private修饰,生成getter和setter方法也是私有的
  • 类的属性如果用val修饰,默认拥有getter方法
  • 可以使用@BooleanBeanProperty注解增加Java方式的getter和setter
嵌套类
  • 在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中定义类。
  • 在Scala中,每个实例都有它自己的Member类,就和它们有自己的members字段一样
  • 要构建一个新的内部对象,你只需要简单的new这个类名:new chatter.Member。
  • class Network { private val members = new ArrayBuffer[Member] def join(name: String) = { val m = new Member(name) members += m m } class Member(val name: String) { val contacts = new ArrayBuffer[Member] } } val chatter = new Network val fred = chatter.join("Fred") val wilma = chatter.join("Wilma") fred.contacts += wilma //OK val barney = myFace.join("Barney") // 类型为myFace .Member fred.contacts += barney // 不可以这样做,不能将一个myFace.Member添加到 chatter.Member元素缓冲当中

    伴生类与伴生对象
  • object的由来
    • 在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。
    • Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。
    • 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。
    • 在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。
    • 类和它的伴生对象可以互相访问其私有成员。
      • 使用 object 时,不用 new ,使用 class 时要 new ,并且 new 的时候, class 中除了方法不执行,其他都执行。
  • 【scala|Scala】scala 中的 object 是单例对象,相当于 java 中的工具类,可以看成是定义静态的方法的类。object 不可以传参数。
继承
  • Scala 使用 extends 关键字来继承一个类。Scala 只允许继承一个父类。
    • 重写一个非抽象方法必须使用override修饰符。
    • 只有主构造函数才可以往基类的构造函数里写参数。
    • 在子类中重写超类的抽象方法时,你不需要使用override关键字。
trait
  • Scala 中 Trait (特征) 相当于 Java 的接口,实际上它比接口还功能强大。
  • 与接口不同的是,它还可以定义属性和方法的实现
  • 一般情况下Scala的类可以继承多个 Trait ,从结果来看就是实现了多重继承。 Trait (特征) 定义的方式与类类似,但它使用的关键字是 trait 。
trait中带属性带方法实现
  • 继承的多个trait中如果有同名的方法和属性,必须要在类中使用 override 重新定义。
  • trait 中不可以传参数
  • trait Read { val readType = "Read" val gender = "m" def read(name:String){ println(name+" is reading") } } trait Listen { val listenType = "Listen" val gender = "m" def listen(name:String){ println(name + " is listenning") } } class Person() extends Read with Listen{ override val gender = "f" } object test { def main(args: Array[String]): Unit = { val person = new Person() person.read("zhangsan") person.listen("lisi") println(person.listenType) println(person.readType) println(person.gender) } }

trait中带方法实现
object test { def main(args: Array[String]): Unit = { val p1 = new Point(1,2) val p2 = new Point(1,3) println(p1.isEqual(p2)) println(p1.isNotEqual(p2)) } } trait Equal{ def isEqual(x:Any) :Boolean def isNotEqual(x : Any) = { !isEqual(x) } } class Point(xx:Int, yy:Int) extends Equal { val x = xx val y = yy def isEqual(p:Any) = { if(p.isInstanceOf[Point]){//判断p是否为[XX]类的实例 val point : Point = p.asInstanceOf[Point]//将p转换为[XX]类的实例 x == point.x && y == point.y }else false } }

特征构造顺序
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。
构造器的执行顺序:
  • 调用超类的构造器;
  • 特征构造器在超类构造器之后、类构造器之前执行;
  • 特征由左到右被构造;
  • 每个特征当中,父特征先被构造;
  • 如果多个特征共有一个父特征,父特征不会被重复构造
  • 所有特征被构造完毕,子类被构造。
构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。
修饰符
  • Scala 访问修饰符基本和Java的一样,分别有:private,protected,public
  • 如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
  • Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
private
  • 用 private 关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。
  • (new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类 Inner 之内。
  • 但在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。
  • Java中允许这两种访问,因为它允许外部类访问内部类的私有成员。
  • 如果字段是私有的,则getter和setter方法也是私有的
  • 如果字段是val,则只有getter方法被生成
  • 如果你不需要任何getter或setter,可以将字段声明为 private[this]
  • class Outer{ class Inner{ private def f(){println("f")} class InnerMost{ f() // 正确 } } (new Inner).f() //错误 }

protected
  • 在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。
  • 在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里
    的其他类也可以进行访问。
  • package p{ class Super{ protected def f() {println("f")} } class Sub extends Super{ f() } class Other{ (new Super).f() //错误 } }

public Scala 中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
作用域保护
  • Scala中,访问修饰符可以通过使用限定词强调。
    • private[x]
    • protected[x]
  • 这里的x指代某个所属的包、类或单例对象。
  • 如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。
键盘标准输入
编程中可以通过键盘输入语句来接收用户输入的数据。(回顾 java 中的 scanner 对象)
在 scala 中只需要导入对应的包,比 java 还要简单不需要实例化对象。
import scala.io.StdIn object Test { def main(args: Array[String]): Unit = { println("请输入姓名") val name = StdIn.readLine() println("请输入年龄") val age = StdIn.readInt() printf("您输入的姓名是%s,年龄是%d",name,age) } }

如上述代码所示, printf 的用法和 java 中一样为格式化输出。使用时注意规范即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9qWWk4XF-1658889492341)(资源/1657958011926.png)]
特殊符号的使用场景
(=>)
  • 表示函数的返回类型(Function Type)
    • 当函数只有一个参数的时候,函数类型里面括起来函数参数的括号是可以省略的。
  • 匿名函数
    • 匿名函数定义, 左边是参数 右边是函数实现体
    • var function0 = () => Math.random() var function1 = (x: Int) => Math.random()

  • case语句
    • val x = 10; val y = 20; val max = x > y match { case true => x case false => y

  • By-Name Parameters(传名参数)
    • 传名参数在函数调用前表达式不会被求值,而是会被包裹成一个匿名函数作为函数参数传递下

    • ef doubles(x: => Int) = { println("Now doubling " + x) println("Now doubling " + x) x * 2 } def f(x: Int): Int = { println(s"Calling f($x)") x }

下划线(_)
  • 作为通配符,类似Java中的*. 如 import scala.math._
  • :_*作为一个整体,告诉编译器你希望将某个参数当做参数序列处理
    • //将 1 to 5 当做参数序列处理 val s = sum(1 to 5:_*)

  • 指代一个集合中的元素
    • //在一个Array a中筛选出偶数, 并乘以2,可以用以下这个办法 a.filter(_%2==0).map(2*_) //要对缓冲数组ArrayBuffer b排序,可以这样: val bSorted = b.sorted(_)

    • 例如a.2 其中句点可以用空格替代
  • 使用模式匹配可以用来获取元组的组员,例如
    • val (first,second,third)=t
    • 但如果不是所有的部件都需要,那么可以在不需要的部件位置上使用,比如上一例中val(first,second,)=t
  • 下划线_代表的是某一类型的默认值.
    • var name = _ (前提必须是变量var 不能是val) 对于Int来说,它的默认值为0 对于Double来说,它的默认值是0.0 对于引用类型来说,它是null.

复杂操作符
  • :: 该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表。用法为 x::list ,其
    中 x 为加入到头部的元素,无论 x 是列表与否,它都只将成为新生成列表的第一个元素,也就是说
    新生成的列表长度为list的长度+1(btw, x::list 等价于 list.::(x) )
  • :++: 两者的区别在于 :+ 方法用于在尾部追加元素, +: 方法用于在头部追加元素,和 :: 很类似,但是 :: 可以用于pattern match ,而 +: 则不行. 关于 +: 和 :+ ,只要记住冒号永远靠近集合类型就OK了。
  • ::: 该方法只能用于连接两个List类型的集合
  • ++ 该方法用于连接两个集合, **list1++list2** 。
  • scala> "A"::"B"::Nil res0: List[String] = List(A, B)scala> "A"+:"B"+:Nil res1: List[String] = List(A, B)scala> Nil:+"A":+"B" res2: List[String] = List(A, B)scala> res0 ++ res1 res3: List[String] = List(A, B, A, B)scala> res0 ::: res1 res4: List[String] = List(A, B, A, B)scala> res0 :: res1 res5: List[java.io.Serializable] = List(List(A, B), A, B)

Scala参数传递
  • Scala的解释器在解析函数参数(function arguments)时有两种方式:
    • 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
      • Call-by-Value 避免了参数的重复求值,效率相对较高
    • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
      • Call-by-Name 避免了在函数调用时刻的参数求值,而将求值推延至实际调用点,但有可
        能造成重复的表达式求值。
Scala函数深化 指定参数名
  • 一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数
    • object Test { def main(args: Array[String]) { printInt(b=5, a=7); } def printInt( a:Int, b:Int ) = { println("Value of a : " + a ); println("Value of b : " + b ); } }

可变参数
  • Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
  • Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。
递归函数
  • 自己调自己
  • 必须有程序的出口
  • /** * 递归函数 10的阶乘 * */ object Test { def main(args: Array[String]) { for (i <- 1 to 10) println(i + " 的阶乘为: = " + factorial(i) ) } def factorial(n: BigInt): BigInt = { if (n <= 1) 1 else n * factorial(n - 1) } }

默认参数值
  • Scala 可以为函数参数指定默认参数值,使用了默认参数
    • 调用函数的过程中如果没有传递参数,函数就会调用它的默认参数值,
    • 调用函数的过程中如果传递了参数,则传递值会取代默认值。
  • /** * 包含默认参数值的函数 * 注意: * 1.默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值 * 2.如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称 */ def fun3(a :Int = 10,b:Int) = { println(a+b) } fun3(b=2)

匿名函数
  • Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
    1. 有参匿名函数
    2. 无参匿名函数
    3. 有返回值的匿名函数
  • 可以将匿名函数返回给val定义的值
  • 匿名函数不能显式声明函数的返回类型
嵌套函数
可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。
/** * 嵌套函数 * 例如:嵌套函数求5的阶乘 */ def fun5(num:Int)={ def fun6(a:Int,b:Int):Int={ if(a == 1){ b }else{ fun6(a-1,a*b) } } fun6(num,1) } println(fun5(5))

偏应用函数
  • Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供
    所需参数。
    • 例如:一个参数是完全相同,另一个参数不同,在这样一个情况之下,我们可以使用偏应用函数来
      进行优化。
/** * 偏应用函数 */ def log(date :Date, s :String)= { println("date is "+ date +",log is "+ s) } val date = new Date() log(date ,"log1") log(date ,"log2") log(date ,"log3") //想要调用log,以上变化的是第二个参数,可以用偏应用函数处理 val logWithDate = log(date,_:String) logWithDate("log1") logWithDate("log2") logWithDate("log3")

高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数
  • Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
    • 函数的参数是函数
    • 函数的返回是函数
    • 函数的参数和函数的返回是函数
bject Test { def main(args: Array[String]) { println( apply( layout, 10) ) } // 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v def apply(f: Int => String, v: Int) = f(v) def layout[A](x: A) = "[" + x.toString() + "]" }/** * 高阶函数 * 函数的参数是函数 或者函数的返回是函数 或者函数的参数和返回都是函数 */ //函数的参数是函数 def hightFun(f : (Int,Int) =>Int, a:Int ) : Int = { f(a,100) } def f(v1 :Int,v2: Int):Int = { v1+v2 } println(hightFun(f, 1)) //函数的返回是函数 //1,2,3,4相加 def hightFun2(a : Int,b:Int) : (Int,Int)=>Int = { def f2 (v1: Int,v2:Int) :Int = { v1+v2+a+b } f2 } println(hightFun2(1,2)(3,4)) //函数的参数是函数,函数的返回是函数 def hightFun3(f : (Int ,Int) => Int) : (Int,Int) => Int = { f } println(hightFun3(f)(100,200)) println(hightFun3((a,b) =>{a+b})(200,200)) //以上这句话还可以写成这样 //如果函数的参数在方法体中只使用了一次 那么可以写成_表示 println(hightFun3(_+_)(200,200))

柯里化函数
  • 柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程
  • 新的函数返回一个以原有第二个参数为参数的函数。
  • 可以理解为高阶函数的简化。和我们文档中上面的函数的返回类是函数的例子进行比较。
/** * 柯里化函数 */ def fun7(a :Int,b:Int)(c:Int,d:Int) = { a+b+c+d } println(fun7(1,2)(3,4))//执行完1+2得出3之后继续往下作为函数执行3+3+4最终求出结果10

Scala集合深化 简介
  • scala.collection 中的所有集合。这些都是高级抽象类或特征,通常具有可变和不可变的实现。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gIh1AJjr-1658889492342)(资源/1658129058916.png)]
  • scala.collection.immutable中的所有集合。不变
  • 不会被改变。但仍可以进行模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CX92H1tY-1658889492342)(资源/1658129141153.png)]
  • scala.collection.mutable中的所有集合。可变
  • 可以在适当的地方被更新或扩展,可以修改,添加,移除一个集合的元素。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xYR8tSFd-1658889492342)(资源/1658129205837.png)]
Trait Traversable
  • Traversable(遍历)是容器(collection)类的最高级别特性(trait),它唯一的抽象操作是foreach:
  • 实现Traversable的容器(collection)类仅仅需要定义与之相关的方法,其他所有方法可都可以从Traversable中继承。
Trait Iterable
  • 容器(collection)结构的上层还有另一个trait。这个trait里所有方法的定义都基于一个抽象方法,迭代器(iterator,会逐一的产生集合的所有元素)。
  • foreach是Traversable所有操作的基础,所以它的性能表现很关键。
  • Iterable有两个方法返回迭代器:grouped和sliding。这些迭代器返回的不是单个元素,而是原容器(collection)元素的全部子序列
    • grouped方法返回元素的增量分块
    • sliding方法生成一个滑动元素的窗口。
Trsit Seq
  • Seq trait用于表示序列。序列,指的是一类具有一定长度的可迭代访问的对象,其中每个元素均带有一个从0开始计数的固定索引位置。
  • 常见操作
    • 索引和长度的操作 (apply、isDefinedAt、length、indices、lengthCompare)
    • 索引检索操作(indexOf、lastIndexOf、indexofSlice、lastIndexOfSlice、indexWhere、
      lastIndexWhere、segmentLength、prefixLength)
      • 返回等于给定值或满足某个谓词的元素的索引。
    • 加法运算(+:,:+,padTo)
      • 在序列的前面或者后面添加一个元素并作为新序列返回。
    • 更新操作(updated,patch)
      • 替换原序列的某些元素并作为一个新序列返回。
    • 排序操作(sorted, sortWith, sortBy)
      • 对序列元素进行排序。
    • 反转操作(reverse, reverseIterator, reverseMap)
      • 将序列中的元素以相反的顺序排列。
    • 比较(startsWith, endsWith, contains, containsSlice, corresponds)
      • 对两个序列进行比较,或者在序列中查找某个元素。
    • 多集操作(intersect, diff, union, distinct)
      • 对两个序列中的元素进行类似集合的操作,或者删除重复元素
  • trait Seq 具有两个subtrait LinearSeq和IndexedSeq。
    • 线性序列具有高效的 head 和 tail 操作
    • 索引序列具有高效的apply, length, 和 (如果可变) update操作。
    • Vector 类提供一个在索引访问和线性访问之间有趣的折中。它同时具有高效的恒定时间的索引开销,和恒定时间的线性访问开销。
//索引和长度 scala> val a = List(1,2,3,4,5,6) a: List[Int] = List(1, 2, 3, 4, 5, 6)scala> a isDefinedAt 0 res0: Boolean = truescala> val b = List(2,3,4) b: List[Int] = List(2, 3, 4)scala> a.lengthCompare(b) res1: Int = 1scala> a.indices res2: scala.collection.immutable.Range = Range 0 until 6//索引搜索 scala> a.lastIndexOf(7) res0: Int = -1scala> a.lastIndexOf(1) res1: Int = 0scala> val b = List(2,3,4) b: List[Int] = List(2, 3, 4)// 加法 scala> a.padTo(10,1) res12: List[Int] = List(1, 2, 3, 4, 5, 6, 1, 1, 1, 1) //更新 scala> a.patch(3,List("a","b"),1) res14: List[Any] = List(1, 2, 3, a, b, 5, 6) scala> a.updated(0,2) res16: List[Int] = List(2, 2, 3, 4, 5, 6) //排序 scala> val a = List(2,1,5,3,6,0) a: List[Int] = List(2, 1, 5, 3, 6, 0) scala> a.sorted res23: List[Int] = List(0, 1, 2, 3, 5, 6)//多集操作 scala> a.intersect(b) res38: List[Int] = List(2, 3) scala> a.diff(b) res40: List[Int] = List(1, 5, 6, 0)

Trait Set
  • 集合是不包含重复元素的可迭代对象。
  • 可变集合和不可变集合提供了+和++操作符来添加元素,-和–用来删除元素。但是这些操作在可变集合中通常很少使用,因为这些操作都要通过集合的拷贝来实现。更有效率的方法是+=和-=,另外还有批量操作符++=和–=。
  • 目前可变集合默认使用哈希表来存储集合元素,非可变集合则根据元素个数的不同,使用不同的方
    式来实现。
    • 空集用单例对象来表示。
    • 元素个数小于等于4的集合可以使用单例对象来表达,元素作为单例对象的字段来存储。
    • 元素超过4个,非可变集合就用哈希前缀树(hash trie)来实现。
    • 采用这种表示方法,较小的不可变集合(元素数不超过4)往往会比可变集合更加紧凑和高效。所以,在处理小尺寸的集合时,不妨试试不可变集合。
  • 集合的两个特质是 SortedSet 和 BitSet.
  • SortedSet 是指以特定的顺序(可以在创建集合时设置顺序)排列其元素(使用iterator或foreach)的集合,默认表示是有序二叉树,即左子树上的元素小于所有右子树上的元素。这样,一次简单的顺序遍历能按增序返回集合中的所有元素。Scala的类 immutable.TreeSet 使
    用红黑树实现,它在维护元素顺序的同时,也会保证二叉树的平衡,即叶节点的深度差最多为1。
  • BitSet是由单字或多字的紧凑位实现的非负整数的集合。其内部使用 Long 型数组来表示。第一个 Long 元素表示的范围为0到63,第二个范围为64到127,以此类推(值为0到127的非可变位集合通过直接将值存储到第一个或第两个 Long 字段的方式,优化掉了数组处理的消耗)。对于每个 Long,如果有相应的值包含于集合中则它对应的位设置为1,否则该位为0。位集合的大小取决于存储在该集合的最大整数的值的大小。假如N是为集合所要表示的最大整
    数,则集合的大小就是 N/64 个长整形字,或者 N/8 个字节,再加上少量额外的状态信息字节。当位集合包含的元素值都比较小时,它比其他的集合类型更紧凑。位集合的另一个优点是它的 contains 方法(成员测试)、+= 运算(添加元素)、-= 运算(删除元素)都非常的高效。
Trait Map
  • Map是一种可迭代的键值对结构。Scala的Predef类提供了隐式转换,允许使用另一种语法:key ->value 代(key,value)
List
  • Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:
    • 列表是不可变的,值一旦被定义了就不能改变,
    • 列表具有递归的结构
  • 构造列表的两个基本单位是 Nil::
    • 不要忘记 Nil 是 长度为0的List
  • Scala列表有三个基本操作:
    • head 返回列表第一个元素
    • tail 返回一个列表,包含除了第一元素之外的其他元素
    • isEmpty 在列表为空时返回true
创建list
// 字符串列表 val site: List[String] = List("Runoob", "Google", "Baidu") // 整型列表 val nums: List[Int] = List(1, 2, 3, 4) // 空列表 val empty: List[Nothing] = List() // 二维列表 val dim: List[List[Int]] = List( List(1, 0, 0), List(0, 1, 0), List(0, 0, 1) )// 字符串列表 val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil)) // 整型列表 val nums = 1 :: (2 :: (3 :: (4 :: Nil))) // 空列表 val empty = Nil // 二维列表 val dim = (1 :: (0 :: (0 :: Nil))) :: (0 :: (1 :: (0 :: Nil))) :: (0 :: (0 :: (1 :: Nil))) :: Nil // 重复数据列表 val site = List.fill(3)("hello") // 通过给定的函数创建 val squares = List.tabulate(6)(n => n * n) println( "一维 : " + squares ) // 创建二维列表 val mul = List.tabulate( 4,5 )( _ * _ ) println( "多维 : " + mul )

list 遍历 for 与 foreach两种方式
//遍历 list.foreach { x => println(x)} list.foreach { println}

list 方法举例
filter :过滤元素
count :计算符合条件的元素个数
map :对元素操作
flatmap :压扁扁平,先map再flat
//filter val list1 = list.filter { x => x>3 } list1.foreach { println} //count val value = https://www.it610.com/article/list1.count { x => x>3 } println(value)//map val nameList = List( "hello beijing", "hello shanghai", "hello guangzhou" ) val mapResult:List[Array[String]] = nameList.map{ x => x.split(" ") } mapResult.foreach{println}//flatmap val flatMapResult : List[String] = nameList.flatMap{ x => x.split(" ") } flatMapResult.foreach { println }

可变长度list
import scala.collection.mutable.ListBufferval listBuffer = ListBuffer[Int](1,2,3,4,5) listBuffer.append(6,7,8,9) //追加元素 listBuffer.+=(10) //在后面追加元素 listBuffer.+=:(100) //在开头追加元素 listBuffer.foreach(println)

Set
  • Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
  • Scala 集合分为可变的和不可变的集合。
    默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用
    scala.collection.mutable.Set 包。
  • Scala集合有三个基本操作:
    • head 返回集合第一个元素
    • tail 返回一个集合,包含除了第一元素之外的其他元素
    • isEmpty 在集合为空时返回true
创建Set
//创建 val set1 = Set(1,2,3,4,4) val set2 = Set(1,2,5)

遍历set 同样两种方式 forforeach
//遍历 set1.foreach { println} for(s <- set1){ println(s) } println("*******")

Set 方法举例
  1. 交集: intersect , &
  2. 差集: diff , &~
  3. 子集: subsetOf
  4. 最大: max
  5. 最小: min
  6. 转成数组: toList
  7. 转成字符串: mkString
/** * 方法举例 */ //交集 val set3 = set1.intersect(set2) set3.foreach{println} val set4 = set1.&(set2) set4.foreach{println} println("*******") //差集 set1.diff(set2).foreach { println } set1.&~(set2).foreach { println } //子集 set1.subsetOf(set2) //最大值 println(set1.max) //最小值 println(set1.min) println("****") //转成数组,list set1.toArray.foreach{println} println("****") set1.toList.foreach{println} //mkString println(set1.mkString) println(set1.mkString("\t"))

可变长度Set
import scala.collection.mutable.Set val set = Set[Int](1,2,3,4,5) set.add(100) set.+=(200) set.+=(1,210,300) set.foreach(println)

Map
  • Map(映射)是一种可迭代的键值对(key/value)结构。
  • 所有的值都可以通过键来获取。
创建Map
  • Map(1 –>"shanghai") 键 -> 值的对应关系创建
  • Map((1,"shanghai")) 元组的形式(key,value)
创建 map 时,相同的 key 被后面的相同的 key 顶替掉,只保留一个。
// 空哈希表,键为字符串,值为整型 var map1:Map[Char,Int] = Map() val map2 = Map( "1" -> "shanghai", 2 -> "shanghai", (3,"beijing") )

获取Map值
  • map.get(“1”).get
  • map.get(100).getOrElse(“no value”) 此种方式如果 map 中没有对应项则赋值为 getOrElse里面的值。
//获取值 println(map.get("1").get) val result = map.get(8).getOrElse("no value") println(result)

遍历Entry 两种方式:for 与 foreach
//map遍历 for(x <- map){ println("====key:"+x._1+",value:"+x._2) } map.foreach(f => { println("key:"+ f._1+" ,value:"+f._2) })

遍历key
//遍历key val keyIterable = map.keys keyIterable.foreach { key => { println("key:"+key+", value:"+map.get(key).get) } } println("---------")

遍历value
//遍历value val valueIterable = map.values valueIterable.foreach { value => { println("value: "+ value) } }

合并Map
//合并map val map1 = Map( (1,"a"), (2,"b"), (3,"c") ) val map2 = Map( (1,"aa"), (2,"bb"), (2,90), (4,22), (4,"dd") ) map1.++(map2).foreach(println)

**注意:**合并 map 会将 map 中的相同 key 的 value 替换
Map方法举例
  1. filter :过滤,留下符合条件的记录
  2. count :统计符合条件的记录数
  3. contains : map 中是否包含某个 key
  4. exist :符合条件的记录存在与否
可变长度Map
import scala.collection.mutable.Map //如果你想要添加一个原来的map中没有的value类型,要么就手动显示[String,Any] val map = Map[String,Any](("name", "lisi"),("money",200)) map+=("age"->18)//单个添加 map+=("age1"->18,"age2"->18,"age3"->18)//多个添加 map-=("age")//删除直接针对key值。map.remove("age")

元组Tuple
元组的定义 大体与列表一样,不同的是元组可以包含不同类型的元素。元组的值是通过将单个的值包含在圆括号中构成的。
创建元组
  1. val tuple1 = new Tuple(1)
  2. val tuple2 = Tuple2(1,2)
  3. val tuple3 =(1,2,3)
  4. 取值用 ._X 可以获取元组中的值
注意:tuple最多支持22个参数。
//创建,最多支持22个 val tuple = new Tuple1(1) val tuple2 = Tuple2("zhangsan",2) val tuple3 = Tuple3(1,2,3) val tuple4 = (1,2,3,4) val tuple18 = Tuple18(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18) val tuple22 = new Tuple22(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22) //使用 println(tuple2._1 + " "+tuple2._2) val t = Tuple2((1,2),("zhangsan","lisi")) println(t._1._2)

遍历元组
  • 虽然元组不是集合,但是在遍历使用时可以当作一个集合来用。
  • 通过 tuple.productIterator 得到迭代器,进而实现遍历。迭代器只能使用一次,下次还想遍历就需要构建一个新的 iterator 。
//遍历 val tupleIterator = tuple22.productIterator while(tupleIterator.hasNext){ println(tupleIterator.next()) }

元组的简单方法
  • 例如 swap , toString 方法。注意 swap 的元素翻转只针对二元组。
/** * 方法 */ //翻转,只针对二元组 println(tuple2.swap)//toString println(tuple3.toString())

Option(选项)
  • Scala Option(选项)类型用来表示一个值是可选的(有值或无值)。
  • Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None 。
val myMap: Map[String, String] = Map("key1" -> "value") val value1: Option[String] = myMap.get("key1") val value2: Option[String] = myMap.get("key2") println(value1) // Some("value1") println(value2) // None

  • getOrElse() 方法来获取元组中存在的元素或者使用其默认的值
val a:Option[Int] = Some(5) val b:Option[Int] = Noneprintln("a.getOrElse(0): " + a.getOrElse(0) ) println("b.getOrElse(10): " + b.getOrElse(10) )

  • isEmpty() 方法来检测元组中的元素是否为 None
val a:Option[Int] = Some(5) val b:Option[Int] = Noneprintln("a.isEmpty: " + a.isEmpty ) println("b.isEmpty: " + b.isEmpty )

WordCount
统计一个数组中每个单词出现的次数
def main(args: Array[String]): Unit = { //定义一个数组 val strings: Array[String] = Array[String]("Hello Word", "Hello Moto", "Hello Word")//正常版 list.flatMap((ele: String) => { ele.split(" ") }).map((ele: String) => { new Tuple2(ele, 1) }).groupBy((ele: (String, Int)) => { ele._1 }).mapValues((list: List[(String, Int)]) => { list.size }).toList.sortBy((tuple: (String, Int)) => { tuple._2 }).foreach(println)//简写模式 strings.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.size).toList.sortBy(_._2).foreach(println) }

模式匹配
  • Scala 提供了强大的模式匹配机制,应用也非常广泛。
  • 一个模式匹配包含了一系列备选项,每个都开始于关键字 case
  • 每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
object HelloMatch { def main(args: Array[String]): Unit = { val tuple = Tuple6(1, 2, 3f, 4, "abc", 55d) val tupleIterator = tuple.productIterator while (tupleIterator.hasNext) { matchTest(tupleIterator.next()) } }/** * 注意点: * 1.模式匹配不仅可以匹配值,还可以匹配类型 * 2.模式匹配中,如果匹配到对应的类型或值,就不再继续往下匹配 * 3.模式匹配中,都匹配不上时,会匹配到 case _ ,相当于default */ def matchTest(x: Any) = { x match { case 1 => println("result is 1") case 2 => println("result is 2") case 3 => println("result is 3") case 4 => println("result is 4") case x: Int => println("type is Int") case x: String => println("type is String") case x: Double => println("type is Double") case _ => println("no match") } } }

偏函数 定义
  • 偏函数(Partial Function),是一个数学概念它不是"函数"的一种, 它跟函数是平行的概念。
  • Scala中的Partia Function是一个Trait,其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果。
  • 如果一个方法中没有 match 只有 case ,这个函数可以定义成 PartialFunction偏函数 。
  • 偏函数定义时,不能使用括号传参,默认定义 PartialFunction 中传入一个值,匹配上了对应的
    case ,返回一个值,只能匹配同种类型。
  • 一个 case 语句就可以理解为是一段匿名函数。
def main(args: Array[String]): Unit = { println(pf(1)) println(pf(6)) println(pf(true)) } def pf: PartialFunction[AnyVal, String] = { case 1 => "One" case 2 => "Two" case 3 => "Three" case i: Int => "Int" case i: Double => "Int" case _ => "Other" }

方法
  • isDefinedAt : 这个函数的作用是判断传入来的参数是否在这个偏函数所处理的范围内。
    • def main(args: Array[String]): Unit = { println(pf.isDefinedAt(1)) println(pf.isDefinedAt(4)) }def pf: PartialFunction[AnyVal, String] = { case 1 => "One" case 2 => "Two" case 3 => "Three" }

  • orElse : 将多个偏函数组合起来使用,效果类似case语句。
    • def main(args: Array[String]): Unit = { println(pf(1)) }def onePf: PartialFunction[Int, String] = { case 1 => "One" }def twoPf: PartialFunction[Int, String] = { case 2 => "Two" }def threePf: PartialFunction[Int, String] = { case 3 => "Three" }def pf = onePf orElse twoPf orElse threePf

  • andThen: 相当于方法的连续调用,比如g(f(x))。
    • def main(args: Array[String]): Unit = { var pf12 = onePf andThen twoPf println(pf12(1)) }def onePf: PartialFunction[Int, String] = { case 1 => "string" }def twoPf: PartialFunction[String, Double] = { case "string" => 2.0 }

  • applyOrElse:它接收2个参数,第一个是调用的参数,第二个是个回调函数。如果第一个调用的参数匹配,返回匹配的值,否则调用回调函数。
    • def main(args: Array[String]): Unit = { println(onePf.applyOrElse(1, { num: Int => "more" })) println(onePf.applyOrElse(2, { num: Int => "more" })) }def onePf: PartialFunction[Int, String] = { case 1 => "one" }

样例类 概念
  • case class是一种可以用来快速保存数据的类,可以认为是java中的pojo类,用于对象数据的保存。
  • 默认实现方法:
    • apply :
      • 不需要使用new关键字就能创建该类对象
    • unapply :
      • 可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配的关键方法。
    • getter /setter :
      • 默认构造参数默认被声明为val,实现了类构造参数的
    • getter方法
      • 如果构造参数是声明为var类型,实现setter和getter方法(不建议)
    • toString : equals : hashCode : copy
      • JavaBean规范的常见方法
实现
object Hello08040018 { def main(args: Array[String]): Unit = { val user: SysUser = SysUser("admin", "123456", "管理员") user match { case SysUser(uname, passwd, nickname) => println(uname, passwd, nickname) } } } case class SysUser(uname: String, passwd: String, nickname: String)

隐式转换 概念
  • 隐式转换是在 Scala 编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在作用范围内自动推导出来合适的类型。
  • 隐式的使用方式
    • 1.将方法或变量标记为implicit
    • 2.将方法的参数列表标记为implicit
    • 3.将类标记为implicit
  • Scala支持两种形式的隐式转换
    • 隐式值:用于给方法提供参数
    • 隐式视图:用于类型间转换或使针对某类型的方法能调用成功
    • 隐式参数:参数列表
Scala隐式值
  • 将p变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺少参数。
  • 如果此时你又在REPL中定义一个隐式变量,再次调用方法时就会报错
  • 隐式转换必须满足无歧义规则,在声明隐式参数的类型是最好使用特别的或自定义的数据类型,不要使用Int,String这些常用类型,避免碰巧匹配
def main(args: Array[String]): Unit = { person("admin") implicit val name1 = "Harry" // implicit val name2 = "Potter" person }//name为隐式参数 def person(implicit name: String) = println(name)

Scala隐式视图
  • 隐式转换函数是指在同一个作用域下面,一个给定输入类型并自动转换为指定返回类型的函数,这个函数和函数名字无关,和入参名字无关,只和入参类型以及返回类型有关。注意是同一个作用域。
  • 隐式转换为目标类型:把一种类型自动转换到另一种类型
    • def main(args: Array[String]): Unit = {hello("str") hello(123) hi("str") hi(123)}def hello(param: String): Unit = { println(param) }def hi(param: String): Unit = { println(param) }implicit def typeConverter(param: Int): String = "yjxxt:" + param implicit def boolean2String(param: Boolean): String = "Boolean:" + param

  • 隐式转换调用类中本不存在的方法
    • 编译器在xiaoming对象调用时发现对象上并没有learn方法,此时编译器就会在作用域范围内查找能使其编译通过的隐式视图
    • 找到learnView方法后,编译器通过隐式转换将对象转换成具有这个方法的对象,之后调用learn方法
class Person { def learn(name: String) = println("正在学习【" + name + "】...") }object Converter { implicit def learnView(b: Boy) = new Person implicit def learnView(g: Girl) = new Person }class Boyclass Girlobject HelloImplicitView { def main(args: Array[String]): Unit = { import com.yjxxt.p0805.Converter._ val xiaoming = new Boy xiaoming.learn("数学") val xiaofang = new Girl xiaofang.learn("数学") } }

Scala隐式类
  • 简介
    • 使用 implicit 关键字修饰的类就是隐式类。若一个 变量A 没有某些方法或者某些变量时,而这个 变量A 可以调用某些方法或者某些变量时
    • 可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入 A 即可。
  • 注意点
    • 隐式类必须定义在类,包对象,伴生对象中。
    • 隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。
  • 具体写法
    • class Rabbit(s:String){ val name = s }object Lesson_ImplicitClass {implicit class Animal(rabbit:Rabbit){ val tp = "Animal" def canFly() ={ println(rabbit.name +" can fly...") } } def main(args: Array[String]): Unit = { val rabbit = new Rabbit("rabbit") rabbit.canFly() println(rabbit.tp) } }

Scala隐式参数
  • 隐式参数
    • 隐式参数同样是编译器在找不到函数需要某种类型的参数时的一种修复机制,我们可以采用显式的柯里化式的隐式参数申明,也可以进一步省略,采用implicitly 方法来获取所需要的隐式变量。
    • 隐式参数相对比较简单,Scala中的函数申明提供了隐式参数的语法,在函数的最后的柯里化参数
    • 列表中可以添加隐式 implicit 关键字进行标记, 标记为 implicit 的参数在调用中可以省略
    • Scala编译器会从当前作用域中寻找一个相同类型的隐式变量,作为调用参数。
  • 注意点
    • 同类型的参数的隐式值只能在作用域内出现一次,同一个作用域内不能定义多个类型一样的隐式值。
    • implicit 关键字必须放在隐式参数定义的开头
    • 一个方法只有一个参数是隐式转换参数时,那么可以直接定义 implicit 关键字修饰的参数,调用时直接创建类型不传入参数即可。
    • 一个方法如果有多个参数,要实现部分参数的隐式转换,必须使用柯里化这种方式,隐式关键字出现在后面,只能出现一次。
  • 具体写法
    • def main(args: Array[String]) { Param.print("jack")("hello") import Context._ Param.print("jack") }object Context { implicit val ccc: String = "implicit" }object Param { def print(content: String)(implicit prefix: String) { println(prefix + ":" + content) } }

Actor Model 概念
Actor Model 是用来编写并行计算或分布式系统的高层次抽象(类似 java 中的 Thread )让程序员不必为多线程模式下共享锁而烦恼。 Actors 将状态和行为封装在一个轻量的进程/线程中,但是不和其他Actors 分享状态,每个 Actors 有自己的世界观,当需要和其他 Actors 交互时,通过发送事件和消息,发送是异步的,非堵塞的(fire-andforget),发送消息后不必等另外 Actors 回复,也不必暂停,每个 Actors 有自己的消息队列,进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,可以建立性能很好的事件驱动系统。
2.12版本后,actor彻底从scala中抽离了出来,所以我们在使用前需要引入相应的lib。
com.typesafe.akka akka-actor_2.12 2.5.9

Actor的特征
  • ActorModel 是消息传递模型,基本特征就是消息传递
  • 消息发送是异步的,非阻塞的
  • 消息一旦发送成功,不能修改
  • 隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。
  • 具体写法
    • class Rabbit(s:String){ val name = s }object Lesson_ImplicitClass {implicit class Animal(rabbit:Rabbit){ val tp = "Animal" def canFly() ={ println(rabbit.name +" can fly...") } } def main(args: Array[String]): Unit = { val rabbit = new Rabbit("rabbit") rabbit.canFly() println(rabbit.tp) } }

Scala隐式参数
  • 隐式参数
    • 隐式参数同样是编译器在找不到函数需要某种类型的参数时的一种修复机制,我们可以采用显式的柯里化式的隐式参数申明,也可以进一步省略,采用implicitly 方法来获取所需要的隐式变量。
    • 隐式参数相对比较简单,Scala中的函数申明提供了隐式参数的语法,在函数的最后的柯里化参数
    • 列表中可以添加隐式 implicit 关键字进行标记, 标记为 implicit 的参数在调用中可以省略
    • Scala编译器会从当前作用域中寻找一个相同类型的隐式变量,作为调用参数。
  • 注意点
    • 同类型的参数的隐式值只能在作用域内出现一次,同一个作用域内不能定义多个类型一样的隐式值。
    • implicit 关键字必须放在隐式参数定义的开头
    • 一个方法只有一个参数是隐式转换参数时,那么可以直接定义 implicit 关键字修饰的参数,调用时直接创建类型不传入参数即可。
    • 一个方法如果有多个参数,要实现部分参数的隐式转换,必须使用柯里化这种方式,隐式关键字出现在后面,只能出现一次。
  • 具体写法
    • def main(args: Array[String]) { Param.print("jack")("hello") import Context._ Param.print("jack") }object Context { implicit val ccc: String = "implicit" }object Param { def print(content: String)(implicit prefix: String) { println(prefix + ":" + content) } }

Actor Model 概念
Actor Model 是用来编写并行计算或分布式系统的高层次抽象(类似 java 中的 Thread )让程序员不必为多线程模式下共享锁而烦恼。 Actors 将状态和行为封装在一个轻量的进程/线程中,但是不和其他Actors 分享状态,每个 Actors 有自己的世界观,当需要和其他 Actors 交互时,通过发送事件和消息,发送是异步的,非堵塞的(fire-andforget),发送消息后不必等另外 Actors 回复,也不必暂停,每个 Actors 有自己的消息队列,进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,可以建立性能很好的事件驱动系统。
2.12版本后,actor彻底从scala中抽离了出来,所以我们在使用前需要引入相应的lib。
com.typesafe.akka akka-actor_2.12 2.5.9

Actor的特征
  • ActorModel 是消息传递模型,基本特征就是消息传递
  • 消息发送是异步的,非阻塞的
  • 消息一旦发送成功,不能修改
  • Actor 之间传递时,接收消息的 actor 自己决定去检查消息, actor 不是一直等待,是异步非阻塞的

    推荐阅读