Scala
Scala介绍
Scala创始人Martin Odersky马丁·奥德斯基
Scala官网https://www.scala-lang.org/介绍了其六大特征。
scala 是一门以 jvm 为运行环境的静态类型编程语言,具备面向对象及函数式编程的特性
Scala特点
- Java 和 scala 可以混编
- 类型推测(自动推测类型)
- 并发和分布式( Actor )
- 特质,特征(类似 java 中 interfaces 和 abstract 结合)
- 模式匹配(类似 java 中的 switch…case )
- 高阶函数
Scala应用场景
- kafka :分布式消息队列,内部代码经常用来处理并发的问题,用scala可以大大简化其代码。
- spark :方便处理多线程场景,另外 spark 主要用作内存计算,经常要用来实现复杂的算法。利用scala 这种函数式编程语言可以大大简化代码
Scala基础学习
定义变量
var
val
数据类型
运算符
没有++,–运算
数字进行±*/四则运算也需要. 来调用【2.+1】
不过为了代码的可阅读性,有语法糖,可以直接四则【2+1】
小数计算的精度丢失
小数在计算机中无法使用二进制精准的表示
使用的IEEE754标准存储,有时候进行计算会发生精度不准
val d1 = 0.1 val d2 = 0.2 println(d1 + d2)
&&,||判断符
位移运算符
>>
、<<
、>>>
、|
、&
等
数学运算符
+= -= *= /= %= =
逻辑语句 if else
Range区间
/**
* 范围 区间
* [a,b] 闭区间 x>=a && x<=b
* [a,b) 左闭右开区间 x>=a && x<b
*
* to 闭区间 a to b
* until 左闭右开区间 a util b
*
* step步长,每次累加的值
* a to/until (b,step)
* 默认Step=1
*/
object Hello10Range {
//三种方式设置rannge
def main(args: Array[String]): Unit = {
// 一:to 闭合区间
var ran1:Range.Inclusive=1 to 10;
ran1.foreach(print);
println();
// 二:unit 左闭右开
var ran2:Range=1 until 10;
ran2.foreach(print)
println();
// 三:通过数组
var ran3=Array.range(1,10);
var ran4=Array.range(1,10,2);
ran3.foreach(print);
println();
ran4.foreach(print);
println();
//设置步长
var ran5=1 to (10,2);
ran5.foreach(print);
println();
}
}
for循环
def main(args: Array[String]): Unit = {
for (i <- 1 to 9) {
println(i)
}
var array = Array[Int](11, 22, 33, 44)
for (i <- 0 to array.length - 1) {
println(array(i))
}
for (i <- array) {
println(i)
}
//双重For循环
for (i <- 1 to 9; j <- 1 to 9) {
println(i + "--" + j)
}
//九九乘法表
for (i <- 1 to 9; j <- 1 to i) {
print(i + " * " + j + " = " + i * j + "\t")
if (i == j) {
println()
}
}
//判断条件
for (i <- 1 to(20, 2); if (i % 3 == 0)) {
println(i)
}
}
Yield收集数据
var result = for {i <- array
if i.contains("1")
} yield i + "666";
result.foreach(println)
var nums = for {i <- 1 to 1000
if i % 3 == 0 && i % 5 == 0 && i % 7 == 0
} yield i;
nums.foreach(println)
方法和函数
方法
def
关键字定义/** 参数列表 * 如果有形参,形参必须声明参数类型 参数数量不固定 * 形参的类型都是val常量类型,不允许”修改“ **/ def 方法名(形参列表):返回值类型 = { //方法体 //return 返回值 }
返回值注意
- 默认会根据返回值自动推断,如果没有返回值就是 unit
- 如果显式的使用return返回数据,需要显式的给予返回值类型
- 如果隐式的返回数据(不加return)。返回值类型默认推断最匹配类型,每一个逻辑的最后一行为返回值
- 如果隐式返回数据也是可以显示的设置返回值类型的,要注意匹配
- 一切的一切都可以归于Unit
函数
//定义函数格式,这是最基本的函数,匿名函数 (参数列表) => 函数体 //可以定义常量给函数起名 val 变量=(参数列表) => 函数体
方法和函数嵌套
特点
1.可以将方法转成函数(隐式 ETA和显式 _) 2.方法可以没有形参的括号,但是函数必须有括号 3.函数也是对象,只要是对象就有引用, val f1 = 方法名 _ 【方法转换为一个函数(显性)】 缺点就在于 当我们直接将变量指向方法的时候,这时的方法不是一个对象
方法中传递函数
//调用方式 method(f1, 100) //定义一个函数,传递一个int类型的值,返回值会被默认推定为String val f1 = (x: Int) => { println("f1:" + x) "abcdefg" } //方法中传递一个函数【这个函数需要的格式,一个int类型形参,返回值是string类型】 和一个int类型的形参 def method(fun: (Int => String), x: Int): Unit = { println("method"); fun(x); } //----------------------------------------------------------------------------- //调用方式 m1(f11, 10, 20) m1((x: Int, y: Int) => {println(x / y)},40, 20) //定义一个函数,需要传递两个int类型的形参,没有返回值 val f11 = (x: Int, y: Int) => { println(x + y) } /** * 声明一个方法,传递一个函数当做形参 * @param f 函数的形式 【(这个函数的形参类型,这个函数的形参类型)=>这个函数的返回值类型】 * @param x int类型的形参 * @param y int类型的形参 */ def m1(f: (Int, Int) => Unit, x: Int, y: Int): Unit = { f(x, y) }
方法转换为函数
//方式1: 显式的转换 //val 函数名: 方法参数类型=>方法返回值类型= 方法名 _ val m2f: Int => String = m1 _ m2f(100); //定义一个方法 def m1(x: Int) = { println("m1:" + x) "abcdefg" } //方法2,隐式转换,在方法调用方法的时候 /** 这时候会根据method需要的类型来决定, 1. 如果参数1是字符串,会先调用m1这个方法获取返回值提供给method执行使用 2. 如下,参数1是一个函数,scala会将方法转换为函数,提供给method执行使用 */ method(m1, 200) //定义方法 def m1(x: Int) = { println("m1:" + x) "abcdefg" } //方法中传递一个函数【这个函数需要的格式,一个int类型形参,返回值是string类型】 和一个int类型的形参 def method(fun: (Int => String), x: Int): Unit = { println("method"); fun(x); }
闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
在一个局部范围,有一个变量的值,在外部一个地方需要使用,就在两者共同的范围内,定义一个变量来接收使用
字符串
//定义了一个字符串
var str: String = "abcdefg"
str = str.concat("yjx")
println(str)
//定义一个可变字符串
var strBui = new StringBuilder("abcdefg")
- 在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
- 在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
- 在 Scala 中,可以使用 String Builder 类创建一个可以修改的字符串
数组
一维数组
//定义数组
var array1 = new Array[Int](10);
//定义数组
var array2 = Array[Int](1, 2, 3, 4, 5);
println("array1是个空数组,不会打印值")
//遍历数组
for (i <- 1 until array1.length) {
array1(i) = i
}
println("array2默认的五个数,遍历打印")
for (e <- array2) {
println(e)
}
可变数组
//需要引入相应包,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)
二维数组/多维数组
//创建二维数组
val array3 = new Array[Array[Int]](4)
array3(0)=Array(1,2);
array3(1)=Array(1,4,5,6,2);
array3(2)=Array(9,6,3,2);
//创建多维数组,数组中默认都是空值
val array: Array[Array[Array[Int]]] = ofDim[Int](3, 3, 3)
for (x <- 1 until 3; y <- 1 until 3; z <- 1 until 3) {
println(array(x)(y)(z))
}
序列
在运算符前后使用: 会去判断前面的类型是否符合Seq(这里是Vector类型),如果是,就不改变直接运算,如果不是,就改变成Seq,开始运算
//数列的相加(:代表了Seq ,如果当前变量不满足Seq就会被拆开)
println("aaa" +: "bbb")
println("aaa" :+ "bbb")
println("aaa" :+ "bbb" :+ "ccc")
println("aaa" :+ "bbb" +: "ccc")
println("aaa" +: "bbb" :+ "ccc")
println("aaa" ++: "bbb" ++: "ccc")
//数组
var array = Array[Int](1, 2, 3, 4, 5);
var value1 = (array :\ 10) ((x: Int, y: Int) => {
var sum = x - y
println(sum)
sum
})
println(value1)
var value2 = (10 /: array) ((x: Int, y: Int) => {
var sum = x + y
println(sum)
sum
})
println(value2)
map
//将数组可以直接.map,转换成map类型
//map和flatMap的区别
//1.map函数的用法,顾名思义,将一个函数传入map中,然后利用传入的这个函数,将集合中的每个元素处理,并将处理后的结果返回
//2.flatMap不一样的地方就是传入的函数在处理完后返回值必须是List,其实这也不难理解,既然是flatMap,那除了map以外必然还有flat的操作,所以需要返回值是List才能执行flat这一步
//定义一个数组
var array1 = Array[String]("Hello", "Word", "Hello", "Yjxxt");
val strings1: Array[Any] = array1.map((x: String) => {
var str:Array[String]=x.split(" ")
})
strings1.foreach(println)
var array2 = Array[String]("Hello Word", "Hello Yjxxt");
val strings2: Array[String] = array2.flatMap((line: String) => {
line.split(" ")
})
strings2.foreach(println)
类和对象
Object和Class的区别
Object中的方法都是静态方法
Class中都是非静态方法,都必须创建对象才能访问
构造器
每个Class类都有一个主构造器,这个主构造器和类交织在一起
除了一个主构造器,类中还可以有任意多的辅助构造器,每一个辅助构造器都必须以一个主构造器或者一个已经存在的构造器开头
//定义一个类,也自带一个主构造器,如果不写小括号的内容,表示无参数的构造器
//例:这里写了一个字符串的参数,表示主构造器是一个带参构造器(参数就是小括号中定义的)
class Teacher(teacherName01: String) {
var teacherName = teacherName01;
var teacherAge = 0;
var teacherGender = "男";
def this() = {
this("")
}
/**辅助构造器*/
def this(_teacherName: String, _teacherAge: Int) = {
//主构造器
this(_teacherName);
//实现自己的业务
this.teacherAge = _teacherAge;
}
//辅助构造器
def this(_teacherName: String, _teacherAge: Int, _teacherGender: String) = {
//主构造器
this(_teacherName, _teacherAge);
//实现自己的业务
this.teacherGender = _teacherGender
}
}
字段属性
-
scala生成的getter方法,就是直接.属性名字
-
类的所有属性默认都会被私有化,但是会生成getter和setter方法,生成getter和setter方法也是私有的
-
类的属性如果用val修饰,默认拥有getter方法
-
可以使用@BooleanBeanProperty(如果是val常量,不会增加get,set)注解增加Java方式的getter和setter
object Hello04Field {
def main(args: Array[String]): Unit = {
var user = new User();
user.passwd = "123456"
println(user.passwd)
user.setReal("管理员")
println(user.real)
user.setReal("");
}
}
class User {
private var uname = ""
var passwd = ""
private val nickname = ""
val address = ""
//变量
@BooleanBeanProperty
var real = ""
//常量
@BooleanBeanProperty
val power = ""
}
嵌套类
-
在scala中,你可以在函数中定义函数,在类中定义类
-
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] } }
伴生类和伴生对象
主要原因:Object中的方法都是静态方法,Class中都是非静态方法,都必须创建对象才能访问
- 类和它的伴生对象可以互相访问其私有成员
- 使用 object 时,不用 new ,使用 class 时要 new ,并且 new 的时候, class 中除了方法
不执行,其他都执行
object aaa {
private var age=19;
private var name="lisi"
def main(args: Array[String]): Unit = {
//可以访问到伴生class的私有属性
println(name+",今年"+age+"岁,性别"+new aaa().sex)
println(new aaa().name+",改了名字叫"+new aaa().nameRe+",今年"+age+"岁,性别"+new aaa().sex)
}
}
class aaa{
private var name="zhangsan";
private val sex='男';
//访问伴生object的属性
private var nameRe=aaa.name;
}
继承
- Scala 使用 extends 关键字来继承一个类。Scala 只允许继承一个父类
- 重写一个非抽象方法必须使用override修饰符。
- 只有主构造函数才可以往基类的构造函数里写参数。
- 在子类中重写超类的抽象方法时,你不需要使用override关键字
object Hello05Extends {
def main(args: Array[String]): Unit = {
val crow: Crow = new Crow
crow.up()
crow.fly()
crow.name = "乌鸦"
println(crow.name)
println(crow)
println(crow.lay())
}
}
abstract class Eggs {
def lay(): String
def up() = {
println("我是一个普通的方法")
}
}
class Bird extends Eggs {
var name = "鸟"
//子类特有方法
def fly(): Unit = {
println("You can fly...")
}
//重写方法
override def lay(): String = {
"Bird蛋"
}
}
class Crow extends Bird {
override def toString(): String = {
"一只乌鸦口渴了"
}
override def lay(): String = {
"Crow 蛋"
}
}
trait
- Scala 中 Trait (特征) 相当于 Java 的接口,实际上它比接口还功能强大。
- 与接口不同的是,它还可以定义属性和方法的实现。
- 一般情况下Scala的类可以继承多个 Trait ,从结果来看就是实现了多重继承。 Trait (特征) 定义的方式与类类似,但它使用的关键字是 trait
trait属性内容
object Hello06Trait {
def main(args: Array[String]): Unit = {
val boy: Boy = new Boy
println(boy.read + "--" + boy.listen)
println(boy.tp)
println(boy.pt)
}
}
trait Read {
var read = 100;
def tp = "read"
val pt = "read"
}
trait Listen {
var listen = 200;
def tp = "listen"
val pt = "listen"
}
class Boy extends Read with Listen {
//处理同名的属性
override def tp = "rl"
override val pt: String = "rl"
}
trait方法内容
object Hello07Trait {
def main(args: Array[String]): Unit = {
val girl: Girl = new Girl
girl.rj()
}
}
trait RJ {
def rj(): Unit = {
println("RJ.rj")
}
}
trait Run extends RJ {
def run() = {
println("run")
}
override def rj(): Unit = {
println("Run.rj")
}
}
trait Jump extends RJ {
def jump() = {
println("run")
}
override def rj(): Unit = {
println("Jump.rj")
}
}
class Girl extends Jump with Run {
// override def rj(): Unit = super[Jump].rj()
override def rj(): Unit = super.rj()
}
trait构造顺序
- 调用超类的构造器;
- 特征构造器在超类构造器之后、类构造器之前执行;
- 特征由左到右被构造;
- 每个特征当中,父特征先被构造;如果多个特征共有一个父特征,父特征不会被重复构造
- 如果多个特征共有一个父特征,父特征不会被重复构造
- 所有特征被构造完毕,子类被构造
修饰符
分别有:private,protected,public
如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员
private可以指定访问包范围
package china {
package shanghai {
private[china] class PuDongXinQu {
protected[china] def miaoJingRoad() {}
class ChuanSha {
private[ChuanSha] val distance = 100
}
private[this] var speed = 200
}
class Test {
private val qu: PuDongXinQu = new PuDongXinQu
}
}
package beijing {
import shanghai._
object visit {
private[beijing] val guide = new PuDongXinQu
guide.miaoJingRoad()
}
}
package guangzhou {
import china.shanghai.PuDongXinQu
object visit {
def main(args: Array[String]): Unit = {
val guide = new PuDongXinQu
guide.miaoJingRoad()
}
}
}
}
键盘获取值
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)
}
}
符号 | 含义 |
---|---|
%d | 十进制数字 |
%s | 字符串 |
%c | 字符 |
%e | 指数浮点数 |
%f | 浮点数 |
Scala参数传递
简单理解:就是在方法A嵌套方法B使用的时候,根据方法A的参数列表,判断是需要方法B直接传递进去调用
还是先计算方法B的值,将方法B得到的值传递方法A去执行
- 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
- Call-by-Value 避免了参数的重复求值,效率相对较高
- 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
- Call-by-Name 避免了在函数调用时刻的参数求值,而将求值推延至实际调用点,但有可能造成重复的表达式求值
def main(args: Array[String]) {
methodA(method01())
println("**********************")
methodB(method01())
}
def method01() = {
println("Method01----")
Math.random();
}
def methodA(num: => Double) = {
println("methodA----")
println("methodA----" + num)
println("methodA----" + num)
}
def methodB(num: Double) = {
println("methodB----")
println("methodB----" + num)
println("methodB----" + num)
}
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 );
}
}
可变参数
object Test {
def main(args: Array[String]) {
//最后一个形参,可以写一个* 表示可以传递多个同类型的参数
printStrings("Runoob", "Scala", "Python");
}
def printStrings( args:String* ) = {
var i : Int = 0;
for( arg <- args ){
println("Arg value[" + i + "] = " + arg );
i = i + 1;
}
}
}
递归函数
自己调用自己
必须有程序的出口
默认参数值
/**
* 包含默认参数值的函数
* 注意:
* 1.默认值的函数中,如果传入的参数个数与函数定义相同,则传入的数值会覆盖默认值
* 2.如果不想覆盖默认值,传入的参数个数小于定义的函数的参数,则需要指定参数名称
*/
def fun3(a :Int = 10,b:Int) = {
println(a+b)
}
fun3(b=2)
匿名函数
- Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
- 有参匿名函数
- 无参匿名函数
- 有返回值的匿名函数
- 可以将匿名函数返回给val定义的值
- 匿名函数不能显式声明函数的返回类型
//完全体
def inc = new Function1[Int,Int]{
def apply(x:Int):Int = x+1;
}
//匿名函数
var inc = (x:Int) => x+1
/**
* 匿名函数
* 1.有参数匿名函数
* 2.无参数匿名函数
* 3.有返回值的匿名函数
* 注意:
* 可以将匿名函数返回给定义的一个变量
*/
//有参数匿名函数
val value1 = (a : Int) => {
println(a)
}
value1(1)
//无参数匿名函数
val value2 = ()=>{
println("我爱中国")
}
value2()
//有返回值的匿名函数
val value3 = (a:Int,b:Int) =>{
a+b
}
println(value3(4,4))
嵌套函数
可以在 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")
集合部分
Traversable是集合的高级抽象类或者特征
- scala.collection.immutable中的所有集合。不变的【在修改元素后会产生新的内容】
- scala.collection.mutable中的所有集合。可变的【可以修改元素】
-
Iterable迭代器
Iterable有两个方法返回迭代器:grouped和sliding。这些迭代器返回的不是单个元素,而是原容器(collection)元素的全部子序列
- grouped方法返回元素的增量分块
- sliding方法生成一个滑动元素的窗口
-
Seq集合
Seq trait用于表示序列。序列,指的是一类具有一定长度的可迭代访问的对象,其中每个元素均带有一个从0开始计数的固定索引位置【和list类似】
-
Set集合
集合是不包含重复元素的可迭代对象
-
Map稽核
Map是一种可迭代的键值对结构。Scala的Predef类提供了隐式转换,允许使用另一种语法:key ->value 代替(key,value)
-
Tuple元祖
存放不同类型值的组合,最好支持22个值
// new Tuple数字,这个数字表示Tuple中存放多少个元素值 val tuple1 = new Tuple1(1) val tuple21 =new Tuple2(1, "a") val tuple3 = new Tuple3(1, "a", new Date()) val tuple4 = new Tuple4(1, "a", new Date(), List(1, 2, 3, 4)) //遍历元组 val productIterator: Iterator[Any] = tuple4.productIterator while (productIterator.hasNext) { println(productIterator.next()) } //单独获取,_4表示第四个元素 println(tuple4._4)
-
Option选择
//Option(选项)类型用来表示一个值是可选的(有值或无值) // 就是:查找的值不存在的时候,打印默认值 def main(args: Array[String]): Unit = { val map: Map[String, String] = Map("key" -> "value") println(map.get("key")) println(map.get("key1")) println(map.getOrElse("key", "666").length) println(map.getOrElse("key1", "666").length) //创建Option测试 val a:Option[Int] = Some(5) val b:Option[Int] = None println("a.getOrElse(0): " + a.getOrElse(0) ) println("b.getOrElse(10): " + b.getOrElse(10) ) }
案例WordCount
object Hello08WordCount {
def main(args: Array[String]): Unit = {
//定义一个数组
val list: List[String] = List[String]("Hello Word", "Hello Moto", "Hello Word")
//1,根据 ” “ 拆分为单个单词
val strings: List[String] = list.flatMap((ele: String) => {
ele.split(" ")
})
//将每个元素拿出来,创建Tuple类型,每个类型中方两个元素,然后将tuplue放进list中
val tuples: List[Tuple2[String,Int]] = strings.map(Tuple2(_, 1))
//tuples将每个元素分组,返回map,内容一致的放在同一个list中,每个list在map中对应的key,设置为当前元素的第一个
println(tuples.groupBy(_._1))
val stringToTuples= tuples.groupBy(_._1)
//mapvalue 将map中value遍历出来,返回值是map,原来key不变,返回值是对应的value
//将map的value遍历出来,然后把value.size设置为value,key不变
val stringToInt: Map[String, Int] = stringToTuples.mapValues(_.size)
stringToInt.foreach(println)
println("-----")
//根据list中第二个参数排序
val list1: List[(String, Int)]= stringToInt.toList.sortBy(_._2)
//正常版
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)
// 简略版
list.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).
mapValues(_.size).toList.sortBy(_._2).foreach(println)
}
}
match模式匹配
- Scala 提供了强大的模式匹配机制,应用也非常广泛
- 一个模式匹配包含了一系列备选项,每个都开始于关键字 case
- 每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式
//可以匹配,参数数据的值,数据类型,和函数类型。。。
def main(args: Array[String]): Unit = {
matchTest(1)
matchTest(2)
matchTest(3)
matchTest("4")
matchTest((x: String,y:String) => {
println(x+"=="+y)
x.length
})
}
//参数是任意类型
def matchTest(param: Any): Unit ={
//参数使用match 关键字进行匹配
param match {
case 1=>println("1")
case 2=>println("2")
case param: Int=>println("int")
case param: String=>println("String")
case param: ((String,String)=>Int) => param("zhangsan","lisi")
case _ => println("其他的东西")
}
}
偏函数
-
偏函数(Partial Function),是一个数学概念它不是"函数"的一种, 它跟函数是平行的概念
-
如果一个方法中没有 match 只有 case ,这个函数可以定义成 PartialFunction偏函数
-
Scala中的Partia Function是一个Trait,其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果
def main(args: Array[String]): Unit = {
//调用Partial Function偏函数 内容
println(pf(1))
println(pf(2))
//判断这个值是否可以case到
println(pf.isDefinedAt(1))
println(pf.isDefinedAt(3))
//整合多个偏函数,本身不用写内容
println(allPf(3))
//两个偏函数嵌套使用
//直接嵌套使用1
println(stringPf(intPf(1)))
//调用偏函数的方法,然后两个函数嵌套使用2
val intStringPf = intPf andThen (stringPf)
println(intStringPf(1))
//偏函数中,一个case捕获多个值
println(week(1))
println(week(6))
println(week(10))
}
def week: PartialFunction[Int, String] = {
case 1 | 2 | 3 | 4 | 5 => "Work"
case 6 | 7 => "Resting"
}
def pf: PartialFunction[Int, String] = {
case 1 => "one"
case 2 => "two"
}
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 allPf = onePf orElse twoPf orElse threePf
def intPf: PartialFunction[Int, String] = {
case 1 => "admin"
}
def stringPf: PartialFunction[String, Double] = {
case "admin" => 2.0
}
case样例类
类似于java中lombok的功能
会提供以下多个方法
- apply :
不需要使用new关键字就能创建该类对象- unapply :
可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配的关键方法。- getter /setter :
默认构造参数默认被声明为val,实现了类构造参数的getter方法
如果构造参数是声明为var类型,实现setter和getter方法(不建议)- toString : equals : hashCode : copy
JavaBean规范的常见方法
object Hello03Case {
def main(args: Array[String]): Unit = {
println("Hello Case")
val user2 = User2("admin", "123456")
user2 match {
case User2(uname, passwd) => println(uname + "--" + passwd + "-----")
}
}
}
class User1(_uname: String, _passwd: String) {
var uname: String = _uname
var passwd: String = _passwd
}
//这个也可以使用,不过其中也会有_uname,_passwd字段
case class User2(_uname: String, _passwd: String) {
var uname: String = _uname
var passwd: String = _passwd
}
//正确的写法,这个类中字段就是这个括号中字段内容,这个括号也就是默认构造器
case class User(uname: String, passwd: String)
隐式转换
-
隐式转换是在 Scala 编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在
作用范围内自动推导出来合适的类型 -
隐式的使用方式
-
将方法或变量标记为implicit
-
将方法的参数列表标记为implicit
-
将类标记为implicit
-
-
Scala支持两种形式的隐式转换
- 隐式值:用于给方法提供参数
- 隐式视图:用于类型间转换或使针对某类型的方法能调用成功
- 隐式参数:参数列表
Scala隐式视图
def main(args: Array[String]): Unit = {
hello(3)
hi(3)
//会找到隐式视图
hello("abcd")
hi("abcd")
}
def hello(num: Int): Unit = {
println(List.fill(num)("e"))
}
def hi(num: Int): Unit = {
println(List.fill(num)("i"))
}
//关心的是 参数的类型和返回值类型
implicit def string2int(string: String): Int = {
string.length
}
Scala隐式类
object Hello08ImplicitClass {
def main(args: Array[String]): Unit = {
val rabbit = new Rabbit("乖乖")
rabbit.canFly()
println(rabbit.tp)
}
//定义一个隐式类,构造器接受Rabbit参数,然后可以将这个类型的对象,转换为当前隐式类类型,调用类中内容
implicit class Animal(rabbit: Rabbit) {
val tp = "Animal"
def canFly() = {
println(rabbit.name + " can fly...")
}
}
}
class Rabbit(s: String) {
val name = s
}
隐式值
隐式值和隐式参数在一起配合使用
object Hello07ImplicitValue {
//隐式的字符串,当需要一个隐式的字符串内容,就使用这个值,不能同时存在多个相同类型的隐式值
implicit val name1: String = "Harry"
// implicit val name2: String = "Potter"
def main(args: Array[String]): Unit = {
person("admin")
//这个方法参数需要一个字符串参数,这个方法中声明了是一个隐式字符串参数,没有传递使用默认的
person
}
//name为隐式参数
def person(implicit name: String) = println(name)
}
Scala隐式参数
隐式值和隐式参数在一起配合使用
object Hello09ImplicitParam {
def main(args: Array[String]): Unit = {
pt("郭靖")("你好")
//引入隐式值所在的object
import org.example.other.ImplicitValue._
pt("郭靖")
}
//设置两个参数,第二个参数为隐式参数,可以不传,然后会去找对应的隐式值
def pt(content: String)(implicit prefix: String) {
println(prefix + ":" + content)
}
}
object ImplicitValue {
implicit val aaa: String = "您好"
}
Actor Model【AKKA发送消息(演员)】
然Akka基于Scala而非Java语言编写而成,但由于Scala最终还是被编译为Java字节码并运行在JVM之上,所以我们可以认为Akka属于Java领域。Akka官方对Akka的介绍如下。
● 是对并发、并行程序的简单的高级别的抽象。
● 是异步、非阻塞、高性能的事件驱动编程模型。
● 是非常轻量的事件驱动处理机制(1GB内存可容纳约270万个Actor)
我们首先导入AKKA依赖
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.12</artifactId>
<version>2.5.9</version>
</dependency>
单个actor案例
import akka.actor.{Actor, ActorSystem, Props}
object Hello10Akka {
def main(args: Array[String]): Unit = {
//构建ActorSystem,任务名字Seaking任意
val actorSystem = ActorSystem("SeaKing")
//开始创建演员,设置处理发送消息对应的类[BestAction] ,bestActor名字任意
val bestActor = actorSystem.actorOf(Props[BestAction], "bestActor")
//开始发送消息,交给对应的Actor去处理
bestActor ! "hello"
bestActor ! "bye"
bestActor ! "let me look look"
}
}
class BestAction extends Actor {
override def receive: Receive = {
//case捕获消息,进行处理
case "hello" => {
println("how are you!")
}
case "bye" => {
println("处理过程,这里也可以调用函数方法等")
}
//其他内容捕获
case _ => {
}
}
}
多个Actor嵌套使用案例
object Hello11Actor {
def main(args: Array[String]): Unit = {
//构建ActorSystem
val actorSystem = ActorSystem("SeaKing")
//开始创建演员
val tangActor = actorSystem.actorOf(Props[TangActor], "tangActor")
//发送消息
while (true) {
tangActor ! "ex"
tangActor ! "cu"
//休眠一秒钟
Thread.sleep(1000)
}
}
}
class TangActor extends Actor {
//创建下级的演员
val exActor = context.actorOf(Props[ExActor], "exActor")
val cuActor = context.actorOf(Props[CuActor], "cuActor")
override def receive: Receive = {
case "ex" => {
exActor ! "我爱你,我还是忘不掉你!"
}
case "cu" => {
cuActor ! "我爱你,她们只是我的妹妹!"
}
}
}
class ExActor extends Actor {
override def receive: Receive = {
case msg: String => {
println("ExActor:" + msg)
}
}
}
class CuActor extends Actor {
override def receive: Receive = {
case msg: String => {
println("CuActor:" + msg)
}
}
}
estActor ! “bye”
bestActor ! “let me look look”
}
}
class BestAction extends Actor {
override def receive: Receive = {
//case捕获消息,进行处理
case “hello” => {
println(“how are you!”)
}
case “bye” => {
println(“处理过程,这里也可以调用函数方法等”)
}
//其他内容捕获
case _ => {
}
}
}
### 多个Actor嵌套使用案例
```scala
object Hello11Actor {
def main(args: Array[String]): Unit = {
//构建ActorSystem
val actorSystem = ActorSystem("SeaKing")
//开始创建演员
val tangActor = actorSystem.actorOf(Props[TangActor], "tangActor")
//发送消息
while (true) {
tangActor ! "ex"
tangActor ! "cu"
//休眠一秒钟
Thread.sleep(1000)
}
}
}
class TangActor extends Actor {
//创建下级的演员
val exActor = context.actorOf(Props[ExActor], "exActor")
val cuActor = context.actorOf(Props[CuActor], "cuActor")
override def receive: Receive = {
case "ex" => {
exActor ! "我爱你,我还是忘不掉你!"
}
case "cu" => {
cuActor ! "我爱你,她们只是我的妹妹!"
}
}
}
class ExActor extends Actor {
override def receive: Receive = {
case msg: String => {
println("ExActor:" + msg)
}
}
}
class CuActor extends Actor {
override def receive: Receive = {
case msg: String => {
println("CuActor:" + msg)
}
}
}