Scala-高阶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
package story.jjw

import java.io.IOException

object Two {

def main(args: Array[String]): Unit = {
// 1. 将函数赋值给变量
sayHelloFunc("JJW")

// 2.匿名函数
sayHelloFuncA("JJW")

// 3.高阶函数
greeting(sayHelloFunc, "SS")

// 4.scala常用高阶函数
// map: 对传入的每个元素都进行映射,返回一个处理后的元素
val arr = Array(1, 2, 3, 4, 5).map(2 * _)
println(arr.toBuffer)

// foreach: 对传入的每个元素都进行处理,但是没有返回值
arr.map(2 * _).foreach(println _)

// filter: 对传入的每个元素都进行条件判断,如果对元素返回true,则保留该元素,否则过滤掉该元素
var arrA = arr.filter(_ % 3 == 0)
println(arrA.toBuffer)

// reduceLeft: 从左侧元素开始,进行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推,即为reduce;reduce操作必须掌握!spark编程的重点!!!
// 下面这个操作就相当于1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9
val sum = (1 to 9).reduceLeft(_ + _)
println(sum)

// sortWith: 对元素进行两两相比,进行排序
val sortArr = Array(3, 2, 5, 4, 10, 1).sortWith(_ - _ > 0)
println(sortArr.toBuffer)

// 5.闭包
// 闭包最简洁的解释:函数在变量不处于其有效作用域时,还能够对变量进行访问,即为闭包
// 两次调用getGreetingFunc函数,传入不同的msg,并创建不同的函数返回
// 然而,msg只是一个局部变量,却在getGreetingFunc执行完之后,还可以继续存在创建的函数之中;greetingFuncHello("JJW"),调用时,值为"hello"的msg被保留在了函数体内部,可以反复的使用
// 这种变量超出了其作用域,还可以使用的情况,即为闭包
// Scala通过为每个函数创建对象来实现闭包,实际上对于getGreetingFunc函数创建的函数,msg是作为函数对象的变量存在的,因此每个函数才可以拥有不同的msg
val greetingFuncHello = getGreetingFunc("hello")
val greetingFuncHi = getGreetingFunc("hi")
greetingFuncHello("JJW")
greetingFuncHi("SS")

// 6.Curring函数
// Curring函数,指的是,将原来接收两个参数的一个函数,转换为两个函数,第一个函数接收原先的第一个参数,然后返回接收原先第二个参数的第二个函数。
// 在函数调用的过程中,就变为了两个函数连续调用的形式
sum1(1, 1)
sum2(1)(1)
sum3(1)(1)

// 7.集合
// Scala中的集合体系主要包括:Iterable、Seq、Set、Map。其中Iterable是所有集合trait的根trai
// Scala中的集合是分成可变和不可变两类集合的,其中可变集合就是说,集合的元素可以动态修改,而不可变集合的元素在初始化之后,就无法修改了。分别对应scala.collection.mutable和scala.collection.immutable两个包。

// 7.1 List
// List代表一个不可变的列表
// List的创建
val list = List(1, 2, 3, 4)
// List有head和tail,head代表List的第一个元素,tail代表第一个元素之后的所有元素,list.head,list.tail
// List有特殊的::操作符,可以用于将head和tail合并成一个List,0 :: list

// 7.2 LinkedList
// LinkedList代表一个可变的列表,使用elem可以引用其头部,使用next可以引用其尾部
// val l = scala.collection.mutable.LinkedList(1, 2, 3, 4, 5); l.elem; l.next

// 7.3 Set
// Set代表一个没有重复元素的集合,Set中的元素是乱序的
var s = Set(1, 2, 3)
s += 4
println(s)

// LinkedHashSet会用一个链表维护插入顺序
val ls = new scala.collection.mutable.LinkedHashSet[Int]();
ls += 1;
ls += 2;
ls += 5

// SortedSet会自动根据key来进行排序
val ss = scala.collection.mutable.SortedSet("orange", "apple", "banana")

// 7.4 Scala的高阶函数,Scala的集合类的map、flatMap、reduce、reduceLeft、foreach等这些函数,就是高阶函数,因为可以接收其他函数作为参数
// map案例实战:为List中每个元素都添加一个前缀
List("Leo", "Jen", "Peter", "Jack").map("name is " + _)

// faltMap案例实战:将List中的多行句子拆分成单词
List("Hello World", "You Me").flatMap(_.split(" "))

// foreach案例实战:打印List中的每个单词
List("I", "have", "a", "beautiful", "house").foreach(println(_))

// zip案例实战:对学生姓名和学生成绩进行关联
List("Leo", "Jen", "Peter", "Jack").zip(List(100, 90, 75, 83))

// Set(1, 2, 3, 4) => Set((1,1), (2,1), (3,1), (4,1))
var sp = s.map((_, 1))
println(sp)
var sp1 = sp.map(_._2)
println(sp1)
var sp2 = sp1.reduceLeft(_ + _)
println(sp2)

// 8.模式匹配
judgeGrade("JJW", "dongdong")
processException(new IOException("报错啦..."))
matchArray(Array("Leo", "JJW", "SS"))
matchList(List("Leo", "JJW", "SS"))

// 9.泛型
// 泛型类
val st = new StudentD[Int](33)
println(st.getSchoolId(52))
// 泛型函数
println(st.getCard[String]("身份证"))

// 上下边界
val steA = new StudentE("JJW")
val steB = new StudentE("SS")
val pa = new PartyA(steA, steB)
pa.play
val pb = new PartyB(steA, steB)
pb.play

// 10.协变和逆变
// enterMeetA(new CardA[Master]("JJW")) 报错,因为方法形参定义的是CardA[Professional] CardA[+T],所以在这里只能是比Professional更子级的类
enterMeetA(new CardA[Professional]("JJW"))
enterMeetB(new CardB[Master]("JJW"))
// enterMeetB(new CardB[Professional]("JJW")) 报错,因为方法形参定义的是CardA[Master] CardA[-T],所以在这里只能是比Master更父级的类

// 11.隐式转换
// Scala的隐式转换,其实最核心的就是定义隐式转换函数,即implicit conversion function。
// 定义的隐式转换函数,只要在编写的程序内引入,就会被Scala自动使用。Scala会根据隐式转换函数的签名,在程序中使用到隐式转换函数接收的参数类型定义的对象时,会自动将其传入隐式转换函数,转换为另外一种类型的对象并返回。

// 隐式转换的发生时机
// 1、调用某个函数,但是给函数传入的参数的类型,与函数定义的接收参数类型不匹配(案例:特殊售票窗口)
// 2、使用某个类型的对象,调用某个方法,而这个方法并不存在于该类型时(案例:超人变身)
// 3、使用某个类型的对象,调用某个方法,虽然该类型有这个方法,但是给方法传入的参数类型,与方法定义的接收参数的类型不匹配(案例:特殊售票窗口加强版)
val stF = new StudentF("JJW")
println(buySpecialTicket(stF))

// 隐式转换-装饰器模式
val leo = new Man("leo")
leo.emitLaser

// 隐式转换-隐式参数
// 定义隐式参数
implicit val signPen = new SignPen
signForExam("JJW")

// 12.Actor
// Scala提供了Actor trait来让我们更方便地进行actor多线程编程,就Actor trait就类似于Java中的Thread和Runnable一样,是基础的多线程基类和接口。我们只要重写Actor trait的act方法,即可实现自己的线程执行体,与Java中重写run方法类似
// 使用start()方法启动actor;使用!符号,向actor发送消息;actor内部使用receive和模式匹配接收消息
// Scala的Actor模型与Java的多线程模型之间,很大的一个区别就是,Scala Actor天然支持线程之间的精准通信;即一个actor可以给其他actor直接发送消息。这个功能是非常强大和方便的。
val userManageActor = new UserManageActor
userManageActor.start()
userManageActor ! Register("JJW", "5233")

// 默认情况下,消息都是异步的;但是如果希望发送的消息是同步的,即对方接受后,一定要给自己返回结果,那么可以使用!?的方式发送消息。即val reply = actor !? message。
// 如果要异步发送一个消息,但是在后续要获得消息的返回值,那么可以使用Future。即!!语法。val future = actor !! message。val reply = future()。

}

// Scala中的函数是一等公民,可以独立定义,独立存在,而且可以直接将函数作为值赋值给变量
// Scala的语法规定,将函数赋值给变量时,必须在函数后面加上空格和下划线
def sayHello(name: String) {
println("Hello, " + name)
}

val sayHelloFunc = sayHello _

// 可以直接定义函数之后,将函数赋值给某个变量;也可以将直接定义的匿名函数传入其他函数之中
// Scala定义匿名函数的语法规则就是,(参数名: 参数类型) => 函数体
val sayHelloFuncA = (name: String) => println("Hello, " + name)

// 直接将某个函数传入其他函数,作为参数,这种函数叫做高阶函数
def greeting(func: (String) => Unit, name: String): Unit = {
func(name)
}

// Scala通过为每个函数创建对象来实现闭包,实际上对于getGreetingFunc函数创建的函数,msg是作为函数对象的变量存在的,因此每个函数才可以拥有不同的msg
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)

// Curring函数
def sum1(a: Int, b: Int) = a + b

def sum2(a: Int) = (b: Int) => a + b

def sum3(a: Int)(b: Int) = a + b

// 模式匹配
// Scala的match case可以匹配各种情况,比如变量的类型、集合的元素、有值或无值
// match case中,只要一个case分支满足并处理了,就不会继续判断下一个case分支了
def judgeGrade(name: String, grade: String) {
grade match {
case "A" => println(name + ", you are excellent")
case "B" => println(name + ", you are good")
case "C" => println(name + ", you are just so so")
// 在case后的条件判断中,不仅仅只是提供一个值,而是可以在值后面再加一个if守卫,进行双重过滤
case _grade if name == "leo" => println(name + ", you are a good boy, come on, your grade is " + _grade)
case _grade => println("you need to work harder, your grade is " + _grade)
// 如果值为下划线,则代表了不满足以上所有情况下的默认情况如何处理
case _ => println("you need to work harder")
}
}

// 模式匹配-类型匹配
// 语法:case 变量: 类型 => 代码
def processException(e: Exception): Unit = {
e match {
case e1: IndexOutOfBoundsException => println("IndexOutOfBoundsException: " + e1)
case e2: IOException => println("IOException: " + e2)
case _e: Exception => println("Exception: " + _e)
}
}

// 对Array进行模式匹配,分别可以匹配带有指定元素的数组、带有指定个数元素的数组、以某元素打头的数组
def matchArray(arr: Array[String]): Unit = {
arr match {
case Array("Leo") => println("Hi, Leo!")
case Array(girl1, girl2, girl3) => println("Hi, girls, nice to meet you. " + girl1 + " and " + girl2 + " and " + girl3)
case Array("Leo", _*) => println("Hi, Leo, please introduce your friends to me.")
case _ => println("hey, who are you?")
}
}

def matchList(list: List[String]): Unit = {
list match {
case "Leo" :: Nil => println("Hi, Leo!")
case girl1 :: girl2 :: girl3 :: Nil => println("Hi, girls, nice to meet you. " + girl1 + " and " + girl2 + " and " + girl3)
case "Leo" :: tail => println("Hi, Leo, please introduce your friends to me.")
case _ => println("hey, who are you?")
}
}

// Scala有一种特殊的类型,叫做Option。Option有两种值,一种是Some,表示有值,一种是None,表示没有值
// Option通常会用于模式匹配中,用于判断某个变量是有值还是没有值,这比null来的更加简洁明了
val grades = Map(("JJW", "A"), ("SS", "A"))

def matchOption(name: String): Unit = {
val grade = grades.get(name)
grade match {
// 有值
case Some(grade) => println("你的成绩是:" + grade)
case None => println("Sorry, your grade information is not in the system")
}
}

// 斜边和逆变示例
def enterMeetA(card: CardA[Professional]) {
println("welcome to have this meeting!")
}

def enterMeetB(card: CardB[Master]) {
println("welcome to have this meeting!")
}

/**
* 隐式转换函数
*
* Scala默认会使用两种隐式转换,一种是源类型,或者目标类型的伴生对象内的隐式转换函数;一种是当前程序作用域内的可以用唯一标识符表示的隐式转换函数
* 如果隐式转换函数不在上述两种情况下的话,那么就必须手动使用import语法引入某个包下的隐式转换函数,比如import test._
*/
implicit def object2SpecialPerson(obj: Object): SpecialPerson = {
if (obj.getClass == classOf[StudentF]) {
val stu = obj.asInstanceOf[StudentF];
new SpecialPerson(stu.name)
}
else if (obj.getClass == classOf[Older]) {
val older = obj.asInstanceOf[Older];
new SpecialPerson(older.name)
}
else Nil
}

var ticketNumber = 0
def buySpecialTicket(p: SpecialPerson) = {
ticketNumber += 1
"T-" + ticketNumber
}

/**
* 隐式转换-装饰器模式
*/
implicit def man2superman(man: Man): Superman = new Superman(man.name)

/**
* 隐式转换-隐式参数的使用示例
*/
def signForExam(name: String) (implicit signPen: SignPen) {
signPen.write(name + " come to exam in time.")
}

}

// 泛型类
class StudentD[T](val localId: T) {
def getSchoolId(id: T) = "S-" + id + "-" + localId

// 泛型函数
def getCard[E](content: E) = {
if (content.isInstanceOf[Int]) {
"card: 001, " + content
} else if (content.isInstanceOf[String]) {
"card: this is your card, " + content
} else {
"card: " + content
}
}
}

// 上边界 <: 符号
// 在指定泛型类型的时候,有时,我们需要对泛型类型的范围进行界定,而不是可以是任意的类型。
class PersonF(val name: String) {
def sayHello = println("Hello, I'm " + name)

def makeFriends(p: PersonF) {
sayHello
p.sayHello
}
}

class StudentE(name: String) extends PersonF(name)

class PartyA[T <: PersonF](p1: T, p2: T) {
def play = p1.makeFriends(p2)
}

// 下边界 >: 符号
// 下边界,即指定泛型类型必须是某个类的父类
class PartyB[T >: StudentE](p1: T, p2: T) {
def play = {
if (p1.getClass == classOf[PersonF]) println("PersonF")
else if (p1.getClass == classOf[StudentE]) println("StudentE")
else println("error")
}
}

/**
* Scala的协变和逆变是非常有特色的!完全解决了Java中的泛型的一大缺憾!
*
* 举例来说,Java中,如果有Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类?答案是:不是。因此对于开发程序造成了很多的麻烦。
*/
class Master

class Professional extends Master

// 大师以及大师级别以下的名片都可以进入会场
class CardA[+T](val name: String)

// 只要专家级别的名片就可以进入会场,如果大师级别的过来了,当然可以了!
class CardB[-T](val name: String)

/**
* 隐式转换
*
* 基础示例
*/
class SpecialPerson(val name: String)

class StudentF(val name: String)

class Older(val name: String)

/**
* 隐式转换-增强类型(装饰器模式)
*/
class Man(val name: String)

class Superman(val name: String) {
def emitLaser = println("emit a laster!")
}

/**
* 隐式参数
*/
class SignPen {
def write(content: String) = println(content)
}

/**
* Actor
* 在scala中,通常建议使用样例类,即case class来作为消息进行发送。然后在actor接收消息之后,可以使用scala强大的模式匹配功能来进行不同消息的处理。
*/
case class Login(username: String, password: String)
case class Register(username: String, password: String)

class UserManageActor extends Actor {
def act(): Unit = {
while (true) {
receive {
case Login(username, password) => println("login, username is " + username + ", password is " + password)
case Register(username, password) => println("Register, username is " + username + ", password is " + password)
}
}
}
}

/**
* 两个Actor之间要互相收发消息
* 一个actor向另外一个actor发送消息时,同时带上自己的引用;其他actor收到自己的消息时,直接通过发送消息的actor的引用,即可以给它回复消息。
*/
case class Message(content: String, sender: Actor)

class JJWTelephoneActor extends Actor {
def act(): Unit = {
while (true) {
receive {
case Message(content, sender) => {
println("JJW telephone: " + content)
sender ! "I'm JJW, please call me after 10 minutes."
}
}
}
}
}

class SSTelephoneActor(val jjwTelephoneActor: Actor) extends Actor {
def act(): Unit = {
jjwTelephoneActor ! Message("Hello JJW, , I'm SS.", this)
receive {
case resp: String => println("ss telephone: " + resp)
}
}
}

最后更新: 2020年08月06日 15:27

原始链接: https://jjw-story.github.io/2020/08/06/Scala-高阶/

× 请我吃糖~
打赏二维码