Scala详细

Scala详细教程

转自:https://blog.csdn.net/wangshun_410/article/details/90759688

目录

Scala详细教程

1.Scala 介绍

1.1 什么是 Scala Scala

 1.2 为什么要学 Scala

2.开发环境准备

2.1 ScalaSDK 安装

2.1.1Window 下安装 ScalaSDK

2.1.2Linux 下安装 ScalaSDK

2.2 IDEA 安装

2.3 IDEAScala 插件的离线安装

2.4 IDEA 创建 HelloScala 工程

3.基本语法

3.1 函数式编程体验 Spark-Shell 之 WordCount

​3.2 Scala 交互模式(cmd 窗口介绍)

3.3 数据类型

3.4 变量的定义

3.5 字符串的格式化输出

3.6 条件表达式

3.7 循环语句和yeild 关键字

3.8 运算符和运算符重载

3.9 方法的定义与调用

3.10 函数的定义与调用

3.11传值调用与传名调用

3.12 可变参数函数

3.13 默认参数值函数

3.14 高阶函数

3.15 部分参数应用函数

3.16 柯里化(Currying)

3.17 偏函数

4.集合的使用

4.1 定长数组和变长数组

4.2 map|flatten|flatMap|foreach 方法的使用

4.3 Seq 序列

4.4 Set 集

4.5  集合常用的方法

4.6并行化集合 par

4.7 Map 和 Option

4.8 案例 wordCount

5. 面向对象

5.1 scala 单例对象

5.2 scala 类

5.2.1 | 类定义 | 主构造器 | 辅助构造器

5.2.2 访问权限

5.2.3 伴生类 | apply 方法

5.3 Trait

5.4 抽象类

5.5 继承

5.5.1final 关键字

5.5.2 type 关键字

5.6 样例类/样例对象

6. 模式匹配 match case

6.1 匹配字符串/类型/守卫

6.2 匹配数组

6.3 匹配集合

6.4 匹配元组

6.5 匹配样例类/样例对象

7.Scala 高级语法

7.1 隐式(implicit)详解

7.1.1 隐式参数

7.1.2 隐式的转换类型

7.1.3 隐式类

7.2 泛型

7.3 类型约束

7.3.1 上界(UpperBounds)/下界(lowerbounds)

7.3.2 视图界定/上下文界定


 

1.Scala 介绍

1.1 什么是 Scala Scala

是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性 。Scala 运 行 于Java平台( Java 虚 拟 机 ), 并 兼 容 现 有 的 Java 程 序 。

 1.2 为什么要学 Scala

  1. 优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API 是否优 雅直接影响用户体验。
  2. 速度快:Scala 语言表达能力强,一行代码抵得上 Java 多行,开发速度快;Scala 是静态编 译的,所以和 JRuby,Groovy 比起来速度会快很多。
  3. 能融合到 Hadoop 生态圈:Hadoop 现在是大数据事实标准,Spark 并不是要取代 Hadoop, 而是要完善 Hadoop 生态。JVM 语言大部分可能会想到 Java,但 Java 做出来的 API 太丑,或者想实现一个优雅的 API 太费劲。

2.开发环境准备

2.1 ScalaSDK 安装

安装 ScalaSDK前 ,请 确 保 已 安 装 JDK1.8+.

2.1.1Window 下安装 ScalaSDK

访问 Scala 官网 https://www.scala-lang.org/download/all.html下载 Scala 编译器安装包,目前大多数的框架都是用 2.11.x 编写开发的,Spark2.x 使用的就是 2.11.x,所以这里推 荐 2.11.x 版本,下载 scala-2.11.8.msi 后点击下一步就可以了。scala-2.11.8.zip需要自己配置环境变量

2.1.2Linux 下安装 ScalaSDK

下载 Scala 地址https://www.scala-lang.org/download/2.12.8.html 然后解压 Scala 到 指定目录 tar -zxvfscala-2.11.8.tgz -C /usr/java 配置环境变量,将 scala 加入到 PATH 中 vi/etc/profile


  
  
    
    
    
    
  1. exportJAVA_HOME=/usr/java/jdk1.8.0_111
  2. exportPATH= $PATH: $JAVA_HOME/bin:/usr/java/scala-2.11.8/bin

 

2.2 IDEA 安装

目前 Scala 的开发工具主要有两种:Eclipse 和 IDEA,这两个开发工具都有相应的 Scala 插件, 如果使用 Eclipse,直接到 Scala 官网下载即可 http://scala-ide.org/download/sdk.html。
由 于 IDEA 的 Scala 插 件 更 优 秀 , 大 多 数 Scala 程 序 员 都 选 择 IDEA , 可 以 到 http://www.jetbrains.com/idea/download/下载社区免费版,点击下一步安装即可,安装时如 果有网络可以选择在线安装 Scala 插件。这里我们使用离线安装 Scala 插件:


1.安装 IDEA,点击下一步即可。由于我们离线安装插件,所以点击 SkipAllandSetDefaul 2.下载 IEDA 的 scala 插件,地址 http://plugins.jetbrains.com/?idea_ce

 

2.3 IDEAScala 插件的离线安装

安装 Scala 插件: Configure->Plugins->Installpluginfromdisk-> 选择 Scala 插件 ->OK-> 重启 IDEA

 

2.4 IDEA 创建 HelloScala 工程

安装完成后, 双击已打开 IDEA, 创建一个新的项目(CreateNewProject)

选中左侧的 Scala->IDEA->Next 

输入项目名称 -> 点击 Finish 完成即可

 

3.基本语法

3.1 函数式编程体验 Spark-Shell 之 WordCount

Q1:对 上 述 文 件 内 容 使 用 Spark进 行 单 词 个 数 统 计 ?


Q2:对 上 述 输 出 结 果 进 行 降 序?


3.2 Scala 交互模式(cmd 窗口介绍)

按住win+R,输入 cmd 命令, 打开 windows 控制台:

输入 scala, 进入 Scalashell 交互模式:

 

3.3 数据类型

Scala 和 Java 一样,有 7 种数值类型 Byte、Char、Short、Int、Long、Float 和 Double(无包 装类型)和 Boolean、Unit 类型.
注意:Unit 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果类型。Unit 只有一个实例值,写成()。

3.4 变量的定义


  
  
    
    
    
    
  1. object 变量定义 extends App {
  2. /**
  3. * 定义变量使用var或者val关 键 字
  4. *
  5. * 语法:
  6. * var | val 变量名称(: 数据类型) =变量值
  7. */
  8. // 使用val修饰的变量, 值不能为修改,相当于java中final修饰的变量
  9. val name = "tom"
  10. // 使用var修饰的变量,值可以修改
  11. var age = 18
  12. // 定义变量时,可以指定数据类型,也可以不指定,不指定时编译器会自动推测变量的数据类型
  13. val name2 : String = "jack"
  14. }

3.5 字符串的格式化输出


  
  
    
    
    
    
  1. /*
  2. * Scala 中的格式化输出
  3. */
  4. object ScalaPrint extends App {
  5. val name = "JackMa"
  6. val price = 998.88d
  7. val url = "www.baidu.com"
  8. // 普通输出,注意这里是可以使用逗号分隔的,但是在java中,我们是需要用“+”号拼接
  9. println( "name=" + name, "price="+price, "url="+url)
  10. // 'f'插值器允许创建一个格式化的字符串,类似于C语言中的printf。
  11. // 在使用'f'插值器时,所有变量引用都应该是printf样式格式说明符,如%d ,%i ,%f等 。
  12. // 这里$name%s打印String变量name,
  13. println(f "姓名:$name%s,价格:$price%1.2f,网址:$url%s")
  14. println(f "姓名:%%s,价格:%%1.1f,网址:%%s",name,price,url)
  15. // 's'插值器允许在字符串中直接使用变量
  16. // 下列语句中将String型变量($name)插入到普通字符串中
  17. println(s "name=$name,price=$price,url=$url")
  18. //'s'插值器还可以处理任意形式的表达式
  19. println(s "1+1=${1+1}") //output "1+1=2"
  20. }

3.6 条件表达式


  
  
    
    
    
    
  1. /*
  2. * Scala if条件表达式
  3. */
  4. object ScalaIf extends App {
  5. //if语句的使用
  6. var faceValue= 98
  7. var res1= if (faceValue> 90) "帅的一批" else "有点恼火"
  8. print(res1)
  9. //3>5 不成立,且代码没有else分支,那么res2应该输出什么呢?
  10. var i= 3
  11. var res2= if (i> 5) i
  12. print (res2) // output ()代表空
  13. // 支持嵌套,if...else if ...else代码过多时可以使用{}
  14. val score= 85
  15. if(score< 60) "不及格"
  16. else if(score>= 60&&score< 70) "及格"
  17. else if (score>= 80&&score< 90) "优秀"
  18. else "优秀"
  19. }

3.7 循环语句和yeild 关键字


  
  
    
    
    
    
  1. /*
  2. * Scala for循环
  3. */
  4. object ScalaFor extends App {
  5. // 定一个数组
  6. var arr=Array( 1, 2, 3, 4, 5, 6)
  7. //遍历数组中么个元素
  8. for(ele <- arr){
  9. print(ele)
  10. } /*output:1 2 3 4 5 6*/
  11. // 0 to 5 =>会生成一个范围集合Range(0,1,2,3,4,5),左闭右闭
  12. for(i <- 0 to 5){
  13. print(i) /*output:0,1,2,3,4,5*/
  14. }
  15. // 0 until 5 =>会生成一个范围集合Range(0,1,2,3,4),左闭右开
  16. for(i <- 0 until 5){
  17. print(i) /*output:0,1,2,3,4*/
  18. }
  19. // for循环中可以增加守卫,下面这段语句是打印arr数组中的偶数
  20. for(i <- arr if i% 2== 0){
  21. print(i)
  22. } /*input:2,4,6*/
  23. //双层for循环
  24. for (i <- 1 to 3;j <- 1 to 3 if i!=i){
  25. print(i* 10+j+ "")
  26. } /*output:12,13,21,23,31,32*/
  27. // yield 关键字将满足条件的e的值又组合成一个数组
  28. var arr2= for(e <- arr if e% 2== 0)
  29. yield e
  30. for (i <- arr2){
  31. print(i) /*output:2,4,6*/
  32. }
  33. }

3.8 运算符和运算符重载

Scala 中的+-*/%等操作符的作用与 Java 一样,位操作符 &|^>><<也一样。

只是有 一点特别的:这些操作符实际上是方法。

例如:

a+b

是如下方法调用的简写:

a.+(b)

a 方法 b 可以写成 a.方法(b)

3.9 方法的定义与调用

方法的返回值类型可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型。


  
  
    
    
    
    
  1. /*
  2. * 方法的定义及调用
  3. * 定义方法的格式为 :
  4. * def methodName ([listofparameters]) : [ returntype ] = { }
  5. * 如果不使用等号和方法体,则隐式声明抽象(abstract)方法 。
  6. */
  7. object ScalaMethod extends App{
  8. // 定义一个sum方法,该方法有两个参数,返回值为int类型
  9. def sum (a:Int, b: Int): Int = { a + b }
  10. // 调用
  11. val result=sum( 1, 5)
  12. print(result)
  13. // 该方法没有任何参数 , 也没有返回值
  14. def sayHello1 = print( "Say BB1")
  15. def sayHello2 () = print( "Say BB2")
  16. sayHello1 // 如果方法没有()调用时不能加()
  17. sayHello2() // 可以省略( ) ,也可以不省略
  18. }

方法可转换为函数:

3.10 函数的定义与调用

函数定义方式一:

调用:f1(2),其中 f1 为函数的引用,也可以叫做函数名。function1 表示一个参数的函数。


函数定义方式二:

下面为没有任何参数的函数,,函数的返回值为 Int 类型。

3.11传值调用与传名调用

通常,函数的参数是传值参数;也就是说,参数的值在传递给函数之前就已经确定。

但是, 在 Scala 中,我们方法或者函数的参数还可以是一个表达式,也就是将一个代码逻辑传递给了某个方法或者函数。


  
  
    
    
    
    
  1. /*
  2. *scala的
  3. * 传名调用( call-by-name)
  4. * 传值调用( call-by-value)
  5. */
  6. object ScalaCallName extends App{
  7. def currentTime ():Long={
  8. System.nanoTime();
  9. }
  10. // 该方法的参数为一个无参的函数,并且函数的返回值为Long
  11. def delayed (f: => Long): Unit ={
  12. print(s "time=${f}")
  13. }
  14. def delayed2 (time: Long): Unit ={
  15. print(s "time=${time}")
  16. }
  17. //调用方式一
  18. delayed(currentTime())
  19. // 调用方式二
  20. // 等价于调用方式一,本质是先计算出参数的值,在带入方法
  21. var time=currentTime()
  22. delayed2(time)
  23. }

3.12 可变参数函数


  
  
    
    
    
    
  1. /*
  2. *scala的可变参数
  3. */
  4. object ScalaVarParams extends App{
  5. //定义一个可变参数方法
  6. def methodManyParams (params:Int*): Unit ={
  7. for (i <- params){
  8. print(i)
  9. }
  10. }
  11. // 调用
  12. methodManyParams( 1, 2, 3, 5)
  13. }

3.13 默认参数值函数


  
  
    
    
    
    
  1. /*
  2. *scala的默认参数
  3. */
  4. object ScalaDefaultParams extends App{
  5. //定义一个默认参数方法
  6. def add (a:Int=1,b:Int=2): Int ={
  7. a+b
  8. }
  9. // 等价于add(a=3,b=2)
  10. add( 3)
  11. // 等价于add(a=4,b=5)
  12. add( 4, 5)
  13. // 等价于add(a=1,b=5)
  14. add(b= 5)
  15. }

3.14 高阶函数


  
  
    
    
    
    
  1. /*
  2. *scala的高阶函数
  3. */
  4. object ScalaHM extends App{
  5. // 高阶函数将其他函数作为参数
  6. def apply (f:Int => Int,p:Int): Unit ={
  7. print(f(p))
  8. }
  9. def fn1 (a:Int): Int ={
  10. a*a
  11. }
  12. apply(fn1, 10) /*output:100*/
  13. }

3.15 部分参数应用函数

如果函数传递所有预期的参数,则表示已完全应用它。 如果只传递几个参数并不是全部参 数,那么将返回部分应用的函数。这样就可以方便地绑定一些参数,其余的参数可稍后填写 补上。


  
  
    
    
    
    
  1. /*
  2. *scala的部分参数
  3. */
  4. object ScalaPartParams extends App{
  5. //定义了一个求和函数
  6. def sum (a:Int,b:Int): Int ={
  7. a+b
  8. }
  9. // 调用sum函数的时候,传入了一个参数a=10,但是b为待定参数。
  10. // sumWithTen形成了一个新的函数,参数个数为一个,类型为int型,
  11. def sumWithTen:Int => Int=sum( 10,_: Int)
  12. //sumWithTen(1),本质是sum(10,1)
  13. print(sumWithTen( 1))
  14. }

3.16 柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新 的函数返回一个以原有第二个参数为参数的函数。


  
  
    
    
    
    
  1. /*
  2. *scala的柯里化
  3. */
  4. object ScalaCurrying extends App{
  5. //定义了一个求和函数
  6. def add(a: Int,b: Int)=a+b
  7. // 那么我们在调用的时候,应该是add(1,2)
  8. // 现在我们将这个函数变形
  9. def add(a: Int)(b: Int)=a+b
  10. // 那么我们在调用的是后应该是add(1)(2),其结果还是等于3,这种方式(或过程)就叫做柯里化,
  11. // 经过柯里化之后函数通用性降低,但是适用性有所提高
  12. //分析下其演变过程
  13. def add(a: Int): Int => Int={
  14. (b: Int)=>a+b
  15. }
  16. //(b:Int)=>a+b 为一个匿名函数,也就意味着add方法的返回值为一个匿名函数,
  17. // 那么现在的调用过程为
  18. var result=add( 1)
  19. var sum1=result( 2)
  20. }

3.17 偏函数

被包在花括号内没有 match 的一组 case 语句是一个偏函数,它是 PartialFunction[A,B]的一个 实例,A 代表参数类型,B 代表返回类型,常用作输入模式匹配。


  
  
    
    
    
    
  1. /*
  2. *scala的偏函数
  3. */
  4. object ScalaPartialFunc extends App{
  5. /*
  6. *PartialFunction[A,B],A 代表参数类型,B 代表返回类型,常用作输入模式匹配。
  7. */
  8. def fun1:PartialFunction[String,Int]={
  9. case "one" => 1
  10. case "two" => 2
  11. case _ => - 1
  12. }
  13. }

4.集合的使用

Scala 的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable 特质 在 Scala 中集合有可变(mutable)和不可变(immutable)两种类型,immutable 类型的集合 初始化后就不能改变了(注意与 val 修饰的变量进行区别。

4.1 定长数组和变长数组


  
  
    
    
    
    
  1. /*
  2. *scala数组
  3. */
  4. import scala.collection.mutable.ArrayBuffer
  5. object ScalaArray{
  6. def main(args: Array[ String]): Unit = {
  7. // 初始化一个长度为8的定长数组,所有元素均为0
  8. val arr= new Array[ Int]( 8)
  9. // 直接打印数组的到的是数组的hashcode值
  10. print(arr)
  11. // 将数组转换成数组缓冲,就可以看到数组中内容
  12. print(arr.toBuffer)
  13. // 定义一个长度为3的定长数组
  14. val arr2= Array( "tom", "jack", "oliver")
  15. // 通过索引获取数组中的值
  16. print(arr2( 1))
  17. // 创建一个变长数组
  18. var arr3=ArrayBuffer[ Int]()
  19. // 末尾追加1
  20. arr3+= 1
  21. // 追加多个元素
  22. arr3+=( 2, 3, 4, 5)
  23. // 追加一个数组
  24. arr3++= Array( 6, 7)
  25. // 追加一个变长数组
  26. arr3++=ArrayBuffer( 8, 9)
  27. // 打印
  28. arr3. foreach( print)
  29. // 在某个位置插入元素
  30. arr3.insert( 0,- 1, 0)
  31. // 移除指定索引元素
  32. arr3.remove( 0, 1)
  33. }
  34. }

4.2 map|flatten|flatMap|foreach 方法的使用


  
  
    
    
    
    
  1. object ScalaArray{
  2. def main( args: Array[ String]): Unit = {
  3. val arr= Array( 1, 2, 3, 4, 5)
  4. // map函数将arr数组中所有元素进行某种映射操作,
  5. // (x:Int)=>x*2为一个匿名函数,x就是数组中的每个元素
  6. val z= arr map( (x:Int)=>x* 2)
  7. // 或者这样写,编译器会推断数据的类型
  8. val y=arr. map( x=>x* 2)
  9. // 亦或者,_表示入参,代表数组中每个元素
  10. val x=arr. map(_ * 2)
  11. println( "--------骚气分割线-----------")
  12. val words= Array( "tom jack oliver jack", "hello tom oliver tom ")
  13. // 将words中元素按照“,”切分
  14. val splitWords: Array[ Array[ String]] = words. map( x=>x. split( ","))
  15. //此时数组中的每个元素进行split操作之后变成了Array,
  16. // flatten是对splitWords里面的元素进行扁平化操作
  17. val flatten: Array[ String] = splitWords. flatten
  18. // 上诉两步操作可以等价于flatmap,意味着先map操作之后进行flatten操作
  19. val result=words. flatMap(_ . split( ","))
  20. // 遍历数组,打印每个元素
  21. result. foreach(print)
  22. }
  23. }

4.3 Seq 序列

不可变的序列 importscala.collection.immutable._

在 Scala 中列表要么为空(Nil 表示空列表)要么是一个 head 元素加上一个 tail 列表。 9::List(5,2) :: 操作符是将给定的头和尾创建一个新的列表


  
  
    
    
    
    
  1. object ScalaListTest{
  2. def main(args: Array[String]): Unit = {
  3. // 创建一个不可变集合
  4. val list=List( 1, 2, 3)
  5. // 创建一个可变集合
  6. val mutalist=ListBuffer( 1, 2, 3)
  7. // 将0插入到list前面生成一个新的list
  8. val list1: List[ Int] = list.:+ ( 0)
  9. val list2: List[ Int] = 0+:list
  10. val list3: List[ Int] = 0::list
  11. val list4: List[ Int] = list.::( 0)
  12. // 将一个元素添加到list后面,形成新的list5
  13. val list5: List[ Int] = list:+( 4)
  14. val list6=List( 4, 5, 6)
  15. // 将两个list合并成一个新的list
  16. val list7: List[ Int] = list++list6
  17. val list8: List[ Int] = list ++: list6
  18. // 将list6插入到list前面形成一个新的集合
  19. val list9: List[ Int] = list.:::(list6)
  20. list8.foreach(println)
  21. }
  22. }

4.4 Set 集

可变的set


  
  
    
    
    
    
  1. object ScalaSetTest{
  2. def main (args: Array[String]): Unit = {
  3. // 创建一个可变set
  4. val set: mutable.HashSet[Int] = new mutable.HashSet[Int]()
  5. // 往set中加入元素1
  6. set+= 1
  7. // add等价于+=
  8. set.add( 2)
  9. // 添加另一个set中元素
  10. set ++= Set( 7, 8, 9)
  11. // 移除一个元素
  12. set-= 2
  13. // remove等价于-=
  14. set.remove( 2)
  15. set.foreach(print)
  16. }
  17. }

不可变的set


  
  
    
    
    
    
  1. object ScalaSetTest{
  2. def main(args: Array[String]): Unit = {
  3. // 创建一个不可变set
  4. val set: HashSet[ Int] = new HashSet[ Int]()
  5. // 将元素和原来的set合并生成一个新的set2,原有set不变
  6. val set2= set+ 1
  7. // 两个set合并生成一个新的set
  8. val set3= set++Set( 4, 5, 6)
  9. set3.foreach(print)
  10. }
  11. }

4.5  集合常用的方法

map, flatten, flatMap, filter, sorted, sortBy, sortWith, grouped, fold(折叠), foldLeft, foldRight, reduce, reduceLeft, aggregate, union, intersect(交集), diff(差集), head, tail, zip, mkString, foreach, length, slice, sum


  
  
    
    
    
    
  1. scala> val list=List( 1, 2, 3, 4, 5)
  2. list: List[Int] = List( 1, 2, 3, 4, 5)
  3. scala> list.map(x=>x* 2)
  4. res0: List[Int] = List( 2, 4, 6, 8, 10)
  5. scala> list.sortBy(x=> -x)
  6. res5: List[Int] = List( 5, 4, 3, 2, 1)
  7. scala> val words=List(( "a", 3),( "b", 5),( "c", 2))
  8. words: List[( String, Int)] = List((a, 3), (b, 5), (c, 2))
  9. scala> words.sortBy(t=> t._2)
  10. res6: List[( String, Int)] = List((c, 2), (a, 3), (b, 5))
  11. scala> words.sortWith((x,y)=>x._2>y._2)
  12. res7: List[( String, Int)] = List((b, 5), (a, 3), (c, 2))
  13. scala> list.grouped( 2).toList
  14. res10: List[List[Int]] = List(List( 1, 2), List( 3, 4), List( 5))
  15. scala> list
  16. res13: List[Int] = List( 1, 2, 3, 4, 5)
  17. scala> list.fold( 0)((x,y)=>x+y)
  18. res14: Int = 15
  19. scala> list.fold( 0)(_+_)
  20. res15: Int = 15
  21. scala> list.foldLeft( 2)(_ - _)
  22. res18: Int = - 13
  23. scala> list.foldRight( 2)(_ - _)
  24. res19: Int = 1
  25. scala> list.reduce((x,y)=> x+y)
  26. res20: Int = 15
  27. scala> list. aggregate( 0)(_ + _,_ + _)
  28. res21: Int = 15
  29. scala> val list=List( 1, 2, 3, 4, 5)
  30. list: List[Int] = List( 1, 2, 3, 4, 5)
  31. scala> val list2=List( 1, 3, 5, 7, 8)
  32. list2: List[Int] = List( 1, 3, 5, 7, 8)
  33. scala> list.union(list2)
  34. res0: List[Int] = List( 1, 2, 3, 4, 5, 1, 3, 5, 7, 8)
  35. scala> list.intersect(list2)
  36. res1: List[Int] = List( 1, 3, 5)
  37. scala> list.diff(list2)
  38. res2: List[Int] = List( 2, 4)
  39. scala> list.head
  40. res3: Int = 1
  41. scala> list.tail
  42. res4: List[Int] = List( 2, 3, 4, 5)
  43. scala> list.zip(list2)
  44. res5: List[(Int, Int)] = List(( 1, 1), ( 2, 3), ( 3, 5), ( 4, 7), ( 5, 8))
  45. scala> list.mkString( "|")
  46. res7: String = 1| 2| 3| 4| 5
  47. scala> list.foreach(print)
  48. 12345
  49. scala> list.length
  50. res11: Int = 5
  51. scala> list.slice( 1, 3)
  52. res12: List[Int] = List( 2, 3)
  53. scala> list.sum
  54. res14: Int = 15

4.6并行化集合 par

调用集合的 par 方法, 会将集合转换成并行化集合


  
  
    
    
    
    
  1. object ScalaParTest{
  2. def main(args: Array[String]): Unit = {
  3. val list: List[ Int] = List( 1, 7, 9, 8, 0, 3, 5, 4, 6, 2)
  4. val par: ParSeq[ Int] = list.par
  5. }
  6. }

4.7 Map 和 Option

在Scala中Option类型样例类用来表示可能存在或也可能不存在的值(Option的子类有Some 和 None)。Some 包装了某个值,None 表示没有值。


  
  
    
    
    
    
  1. object ScalaMapTest{
  2. def main (args: Array[String]): Unit = {
  3. val map: Map[String, Int] = Map( "a"-> 1, "b"-> 2, "c"-> 3)
  4. val i: Int = map( "d")
  5. // map的get方法返回值Option,意味着maybeInt可能取到值也可能没有取到值
  6. val maybeInt: Option[Int] = map.get( "d")
  7. // 如果maybeInt=None时会抛出异常
  8. val v=maybeInt.get
  9. // 第一个参数为要获取的key
  10. // 第二个参数为如果没有这个key返回一个默认值
  11. val ele: Int = map.getOrElse( "d",- 1)
  12. }
  13. }

4.8 案例 wordCount


  
  
    
    
    
    
  1. object ScalaWordCount{
  2. def main(args: Array[ String]): Unit = {
  3. // 第一种方式
  4. val words= Array( "hello tom jack oliver", "tom jack jim hello")
  5. val strings: Array[ String] = words.flatMap(_ .split( " "))
  6. val tuples: Array[( String, Int)] = strings.map((_ , 1))
  7. val stringToTuples: Map[ String, Array[( String, Int)]] = tuples.groupBy(_._1)
  8. val stringToInt = stringToTuples.mapValues(_.length)
  9. stringToInt. foreach( print)
  10. // 第二种方式
  11. val list= words.flatMap(_.split( " ")) //对数组中每个元素进行切分,并进行扁平化操作
  12. .map((_, 1)) // 将数组中每个元素转换成元组,元组第二个元素为1
  13. .groupBy(_._1) // 将相同单词元组分为一组
  14. .mapValues(_.length) //对每个key的value集合进行长度操作
  15. .toList // 转换成list
  16. list. foreach( print)
  17. // 第三种方式
  18. val list2: List[( String, Int)] = words.flatMap(x=>x.split( " ")).groupBy(x=>x).map(t=>(t._1,t._2.length)).toList
  19. list2. foreach( print)
  20. }
  21. }

5. 面向对象

5.1 scala 单例对象

在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那 就是使用关键字 object,object 对象不能带参数。


  
  
    
    
    
    
  1. /*
  2. * 单例对象
  3. */
  4. object ScalaSingleton{
  5. def saySomeThing (msg:String): Unit ={
  6. println(msg)
  7. }
  8. }
  9. object test{
  10. def main (args: Array[String]): Unit = {
  11. ScalaSingleton.saySomeThing( "hello")
  12. println(ScalaSingleton)
  13. println(ScalaSingleton)
  14. // output:
  15. // hello
  16. //ScalaSingleton$@ea4a92b
  17. //ScalaSingleton$@ea4a92b
  18. }
  19. }

5.2 scala 类

5.2.1 | 类定义 | 主构造器 | 辅助构造器

类定义


  
  
    
    
    
    
  1. /*
  2. * scala中,类并不用声明为public
  3. * 如果你没有定义构造器,类会有一个默认的空参构造器
  4. *
  5. * var 修饰的变量,对外提供setter、getter方法
  6. * val 修饰的变量,对外只提供getter方法,不提供setter方法
  7. */
  8. class Student{
  9. // _表示一个占位符,编译器会根据你变量的类型赋予相应的初始值
  10. var name:String=_
  11. // 错误代码,val修饰的变量不能使用占位符
  12. // val age:Int=_
  13. val age:Int= 10
  14. }
  15. object test{
  16. def main (args: Array[String]): Unit = {
  17. // 空参构造器可以加()也可以不加
  18. val student = new Student()
  19. student.name= "JackMa"
  20. // 错误代码,类中使用val修饰的变量不能更改
  21. // student.age=20
  22. println(s "name=${student.name},age=${student.age}")
  23. }
  24. }

定义在类后面的为类主构造器, 一个类可以有多个辅助构造器


  
  
    
    
    
    
  1. /*
  2. * 定义在类名称后面的构造器为主构造器
  3. * 类的主构造器中的属性会定义成类的成员变量
  4. * 类的主构造器中属性没有被var|val修饰的话,该属性不能访问,相当于对外没有提供get方法
  5. * 如果属性使用var修饰,相当于对外提供set和get方法
  6. */
  7. class Student1( var name:String,val age:Int){
  8. var gender:String=_
  9. def this (name:String,age:Int,gender:String){
  10. this(name,age)
  11. this.gender=gender
  12. }
  13. }
  14. object test{
  15. def main (args: Array[String]): Unit = {
  16. val s1 = new Student1( "Tom", 18)
  17. println(s "name=${s1.age},age=${s1.age},gender=${s1.gender}")
  18. val s2 = new Student1( "JackMa", 20, "man")
  19. println(s2.gender)
  20. // output:
  21. // name=18,age=18,gender=null
  22. // man
  23. }
  24. }

5.2.2 访问权限

  • 构造器的访问权限

  
  
    
    
    
    
  1. /*
  2. * private 加在主构造器之前,这说明该类的主构造器是私有的,外部对象或者外部类不能访问
  3. * 也适用与辅助构造器
  4. */
  5. class Student2 private( var name:String,val age:Int){
  6. var gender:String=_
  7. private def this (name:String,age:Int,gender:String){
  8. this(name,age)
  9. this.gender=gender
  10. }
  11. }
  • 成员变量的访问权限

  
  
    
    
    
    
  1. /*
  2. * private val age
  3. * age 在本类中有setter和getter方法
  4. * 但是加上private 也就意味着age只能在这个类的内部及其伴生类中可以修改
  5. */
  6. class Student3 private(){
  7. private var name:String=_
  8. // 伴生类可以访问
  9. private var age: Int=_
  10. // private [this]关键字标识给属性只能在类内部访问,伴生类不能访问
  11. private [ this] var gender:String= "man"
  12. }
  13. // 伴生类
  14. object Student3{
  15. def main(args: Array[String]): Unit = {
  16. val student = new Student3()
  17. // 伴生对象可以访问
  18. student.name= "jack"
  19. student.age = 20
  20. println(s "name=${student.name},age=${student.age}")
  21. // 伴生类不能访问
  22. //println(student.gender)
  23. // output:
  24. // name=jack,age=20
  25. }
  26. }
  • 类包的访问权限

  
  
    
    
    
    
  1. /*
  2. * private [this] class放在类声明最前面,是修饰类的访问权限,也就是说类在某些包下可见或不能访问
  3. * private [sheep] class代表该类在sheep包及其子包下可见,同级包不能访问
  4. */
  5. private [ this] class Student4 ( val name:String, private var age: Int){
  6. var gender :String=_
  7. }
  8. // 伴生类
  9. object Student4{
  10. def main(args: Array[String]): Unit = {
  11. val s = new Student4( "JackMa", 18)
  12. print(s.age)
  13. }
  14. }

5.2.3 伴生类 | apply 方法

在 Scala 中, 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象。必须 在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类。类和它的伴生对象可以互相访问其私有成员。


  
  
    
    
    
    
  1. /*
  2. * 定义一个apply方法
  3. * object对象中可以对apply进行各种重载
  4. */
  5. object test{
  6. def main(args: Array[ String]): Unit = {
  7. val v = test( 2, 3) // 语法糖
  8. print(v)
  9. //output:5
  10. }
  11. def apply(a: Int,b: Int)={
  12. a+b
  13. }
  14. }

5.3 Trait

ScalaTrait(特质) 相当于 Java 的接口,实际上它比接口还功能强大。 与接口不同的是,它还可以定义属性和方法的实现。 一般情况下 Scala 的类只能够继承单一父类,但是如果是 Trait(特质) 的话就可以继承多个,实现了多重继承。使用的关键字是 trait。


  
  
    
    
    
    
  1. trait ScalaTrail {
  2. // 定义了一个属性
  3. var pro: Int= 666
  4. // 定义一个没有实现的方法
  5. def sayHello(name: String)
  6. // 定义了一个带具体实现的方法
  7. def small(name: String): Unit ={
  8. println( s"太阳对${name}笑")
  9. }
  10. }
  11. object ScalaTrailImpl extends ScalaTrail {
  12. // 实现方法时可以有override关键字,也可以没有
  13. def sayHello(name: String): Unit = {
  14. println( s"hello $name")
  15. }
  16. // 重写方法时必须得有override关键字
  17. override def small(name: String): Unit = {
  18. println( s"$name like small")
  19. }
  20. }
  21. object test extends App{
  22. ScalaTrailImpl.sayHello( "Oliver")
  23. // 如果ScalaTrailImpl没有重写small方法,则调用ScalaTrail中已经实现了的方法
  24. // 如果ScalaTrailImpl重写了small方法,则调用的是ScalaTrailImpl中的方法
  25. ScalaTrailImpl.small( "wang")
  26. // output:
  27. // hello Oliver
  28. // wang like small
  29. }

动态混入特质,使用with关键字


  
  
    
    
    
    
  1. trait ScalaTrail {
  2. // 定义了一个属性
  3. var pro:Int= 666
  4. // 定义一个没有实现的方法
  5. def sayHello (name:String)
  6. // 定义了一个带具体实现的方法
  7. def small (name:String): Unit ={
  8. println(s "太阳对${name}笑")
  9. }
  10. }
  11. class Student {
  12. }
  13. object test extends App{
  14. val student: Student with ScalaTrail = new Student with ScalaTrail {
  15. override def sayHello (name: String): Unit = {
  16. println(f "name=$name")
  17. }
  18. }
  19. student.sayHello( "Jack")
  20. }

5.4 抽象类

在 Scala 中, 使用 abstract 修饰的类称为抽象类. 在抽象类中可以定义属性、未实现的方法和 具体实现的方法。


  
  
    
    
    
    
  1. abstract class Animal{
  2. // 定义了一个属性
  3. var name: String= "animal"
  4. // 定义一个未实现方法
  5. def sleep()
  6. // 定义一个带具体实现方法
  7. def eat(f: String): Unit ={
  8. println(s "eating $f")
  9. }
  10. }

5.5 继承

继承是面向对象的概念,用于代码的可重用性。 被扩展的类称为超类或父类, 扩展的类称 为派生类或子类。Scala 可以通过使用 extends 关键字来实现继承其他类或者特质。


  
  
    
    
    
    
  1. /*
  2. * with 后面只能是特质
  3. * 父类未实现的方法,子类必须实现
  4. * 父类已经实现的方法,子类要重写该方法,必须使用override关键字
  5. */
  6. abstract class Animal{
  7. // 定义了一个属性
  8. var name: String= "animal"
  9. // 定义一个未实现方法
  10. def sleep()
  11. // 定义一个带具体实现方法
  12. def eat(f: String): Unit ={
  13. println( s"eating $f")
  14. }
  15. }
  16. class Dog extends Animal {
  17. override def sleep(): Unit = {
  18. println( "耳朵贴地睡")
  19. }
  20. }
  21. object test extends App{
  22. private val dog = new Dog
  23. dog.sleep();
  24. dog.eat( "bone")
  25. // output:
  26. // 耳朵贴地睡
  27. // eating bone
  28. }

5.5.1final 关键字

 

  • 被 final 修饰的类不能被继承;
  • 被 final 修饰的属性不能重写;
  • 被 final 修饰的方法不能被重写。

5.5.2 type 关键字

Scala 里的类型,除了在定义 class,trait,object 时会产生类型,还可以通过 type 关键字来声明 类型。
type 相当于声明一个类型别名:


  
  
    
    
    
    
  1. object test extends App{
  2. // 把String类型用S代替
  3. type S = String
  4. var name : S= "Oliver"
  5. println(name)
  6. }

通常 type 用于声明某种复杂类型,或用于定义一个抽象类型。


  
  
    
    
    
    
  1. class A{
  2. type T
  3. def fn(t: T): Unit ={
  4. println(t)
  5. }
  6. }
  7. class B extends A{
  8. override type T = Int
  9. }
  10. class C extends A{
  11. override type T = String
  12. }
  13. object test extends App{
  14. private val b = new B()
  15. private val c = new C()
  16. b.fn( 3)
  17. c.fn( "Hello")
  18. }

5.6 样例类/样例对象


  
  
    
    
    
    
  1. /*
  2. * 样例类,使用 case关键字修饰的类,其重要的就是支持模式匹配
  3. * 样例类: case class 类名(属性)
  4. * 类名定义必须是驼峰式,属性名称第一个字母小写
  5. */
  6. case class Message(sender: String,massageContent: String)
  7. /*
  8. * 样例对象不能封装数据
  9. */
  10. case object CheckHeartBeat

 

6. 模式匹配 match case

6.1 匹配字符串/类型/守卫


  
  
    
    
    
    
  1. object test extends App{
  2. /*
  3. * 匹配字符串
  4. */
  5. def contentMatch(str: String)=str match {
  6. case "dog"=>println( "小狗")
  7. case "cat"=>println( "小猫")
  8. case "1"=>println( "数字1")
  9. case _ => println( "匹配失败")
  10. }
  11. contentMatch( "cat")
  12. contentMatch( "1")
  13. contentMatch( "2")
  14. // output:
  15. // 小猫
  16. // 数字1
  17. // 匹配失败
  18. /*
  19. * 匹配类型
  20. */
  21. def typeMatch(ele: Any)=ele match {
  22. case x: Int=>println( s"Int:${x}")
  23. case y: Double=>println( s"Double:${y}")
  24. case z: String=>println( s"String:${z}")
  25. case _ =>println( "match failure")
  26. }
  27. typeMatch( "helo")
  28. typeMatch( 2)
  29. typeMatch( 2d)
  30. // output:
  31. // String:helo
  32. // Int:2
  33. // Double:2.0

6.2 匹配数组


  
  
    
    
    
    
  1. object test extends App{
  2. /*
  3. * 匹配数组
  4. */
  5. def arrayMatch(arr: Any)=arr match {
  6. case Array( 0)=>println( "只有一个0元素的数组")
  7. case Array( 0,_)=>println( "以0开头,拥有两个元素的数组")
  8. case Array( 1,_, 3)=>println( "以1开头,3结尾的任意三个元素的数组")
  9. case Array(_*)=>println( "N个元素的数组")
  10. }
  11. arrayMatch( Array( 0))
  12. arrayMatch( Array( 0, 2))
  13. arrayMatch( Array( 1, true, 3))
  14. arrayMatch( Array( 1, 3, 5, 7, 9))
  15. // output:
  16. // 只有一个0元素的数组
  17. // 以0开头,拥有两个元素的数组
  18. // 以1开头,3结尾的任意三个元素的数组
  19. // N个元素的数组
  20. }

6.3 匹配集合


  
  
    
    
    
    
  1. object test extends App{
  2. /*
  3. * 匹配集合
  4. */
  5. def listMatch(list: Any)=list match {
  6. case 0:: Nil=>println( "只有0元素的集合")
  7. case 7:: 9:: Nil=>println( "只有7和9两个元素的集合")
  8. case x::y::z:: Nil=>println( s"只有三个元素集合${x},${y},${z}")
  9. case m::n=>println( s"拥有head和tail的集合。head:${m},tail:${n}")
  10. }
  11. listMatch( List( 0))
  12. listMatch( List( 7, 9))
  13. listMatch( List( 1, 2, 3))
  14. listMatch( List( 8, 7, 6, 5, 4))
  15. // output:
  16. // 只有0元素的集合
  17. // 只有7和9两个元素的集合
  18. // 只有三个元素集合1,2,3
  19. // 拥有head和tail的集合。head:8,tail:List(7, 6, 5, 4)
  20. }

6.4 匹配元组


  
  
    
    
    
    
  1. object test extends App{
  2. /*
  3. * 匹配元组
  4. */
  5. def tupMatch(tup: Any)=tup match {
  6. case ( 3,x,y)=>println( "第一个元素为3的元组")
  7. case (_, 2)=>println( "第二个元素为2,拥有两个元素的数组")
  8. case (x,y,z)=>println( "拥有三个元素的任意元组")
  9. }
  10. tupMatch(( 3, 2, 1))
  11. tupMatch(( 3, 2))
  12. tupMatch(( 4, 2, 1))
  13. // output:
  14. // 第一个元素为3的元组
  15. // 第二个元素为2,拥有两个元素的数组
  16. // 拥有三个元素的任意元组
  17. }

6.5 匹配样例类/样例对象


  
  
    
    
    
    
  1. case object CheckTimeOutTask
  2. case class SubmitTask(id:String,name:String)
  3. case class HeartBeat(time:Long)
  4. object test extends App{
  5. /*
  6. * 匹配样例类,样例对象
  7. */
  8. def coMatch (ele:Any)=ele match {
  9. case SubmitTask (id,name)=>println(s "submit task-id:${id},task-name:${name}")
  10. case CheckTimeOutTask=>println( "checking.....")
  11. case HeartBeat (time)=>println(s "time is ${time}")
  12. }
  13. coMatch(SubmitTask( "001", "node1"))
  14. coMatch(CheckTimeOutTask)
  15. coMatch(HeartBeat( 8888888L))
  16. coMatch(HeartBeat( 6666))
  17. // output:
  18. // submit task-id:001,task-name:node1
  19. // checking.....
  20. // time is 8888888
  21. // time is 6666
  22. }

 

 

7.Scala 高级语法

 

7.1 隐式(implicit)详解

  • 隐式参数 
  • 隐式转换类型
  • 隐式类

7.1.1 隐式参数

定义方法时,可以把参数列表标记为 implicit,表示该参数是隐式参数。一个方法只会有一 个隐式参数列表,置于方法的最后一个参数列表。如果方法有多个隐式参数,只需一个 implicit 修饰即可。
譬如:deffire(x:Int)(implicita:String,b:Int=9527)
当调用包含隐式参数的方法是,如果当前上下文中有合适的隐式值,则编译器会自动为该 组参数填充合适的值,且上下文中只能有一个符合预期的隐式值。如果没有编译器会抛出 异常。当然,标记为隐式参数的我们也可以手动为该参数添加默认值。


  
  
    
    
    
    
  1. object test extends App{
  2. def say( implicit content: String)=println(content)
  3. /*
  4. * say方法时隐式参数,如果你没有传参数
  5. * 编译器在编译的时候会自动的从当前上下文中找一个隐式值(符合参数类型的隐式值)
  6. */
  7. say( "good morning")
  8. implicit val content= "Are you ok ?"
  9. say
  10. // output:
  11. // good morning
  12. // Are you ok ?
  13. }

7.1.2 隐式的转换类型

使用隐式转换将变量转换成预期的类型是编译器最先使用 implicit 的地方。当编译器看到类 型 X 而却需要类型 Y,它就在当前作用域查找是否定义了从类型 X 到类型 Y 的隐式定义。


  
  
    
    
    
    
  1. object test extends App{
  2. //var i:Int=3.14/*此时程序会报错*/
  3. implicit def double2Int( d: Double)=d.toInt
  4. // i是int类型,但是赋值的时候缺是一个浮点型,此时编译器会在当前上下文中找一个隐式转换,
  5. // 找一个double转换成int的隐式转换
  6. var i: Int= 3.14
  7. println(i)
  8. }

7.1.3 隐式类

在 Scala 中,我们可以在静态对象中使用隐式类。


  
  
    
    
    
    
  1. object test extends App{
  2. implicit class Calculator(x:Int){
  3. def add(y: Int)={

你可能感兴趣的:(#,spark,scala,开发语言,后端)