Scala

作者:高志远

微信:gzy2001

Hadoop生态体系知识串讲

在这里插入图片描述

Scala编程语言

一、概述

http://scala-lang.org

专门为计算而生的语言,Scala将(Java后者C++)面向对象设计函数式编程结合在一起的简洁的高级编程语言。而函数式编程强调的是通过传递算子(代码|函数)实现大规模数据集的本地计算。Scala虽然是一门独立的编程语言,但是它可以无缝和Java语言对接。Scala编译的代码可以直接运行在JVM之上。Spark 和 Scala 能够紧密集成,例如 使用Scala语言操作大数据集合的时候,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。

.java源文件 ---> java编译器 .class ---> jvm

.scala源文件 ---> scala编译器 .class ---> jvm

二、环境搭建

安装

准备工作

建议JDK1.8以上

C:\Users\Administrator>java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
Scala安装

后续课程所使用的Scala版本:scala-2.11.12.msi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnTApE0f-1575721948433)(assets/1573787952798.png)]

配置环境变量
# SCALA_HOME  C:\scala
# PATH		  C:\scala\bin
测试使用
C:\Users\Administrator>scala -version
Scala code runner version 2.11.12 -- Copyright 2002-2017, LAMP/EPFL

三、使用方式

命令窗口

测试使用

REPL命令窗口: R(Read 读取)、E(Evaluation 评估求值)、P(Print 打印)、L(Loop 循环)

C:\Users\Administrator>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.

scala> 1+1
res0: Int = 2

scala> 1+2
res1: Int = 3

1+1 表达式 执行后返回一个名为res0 变量,类型为Int , 计算结果为2

传统方式

使用文本编辑器 写Scala源文件,手动编译 解释执行

编译指令:scalac *.scala

解释执行:scala *

D:\>scalac HelloWorld3.scala

D:\>scala HelloWorld3
Hello World
object HelloWorld3{
	def main(args:Array[String]):Unit = {
		println("Hello World")
	}
}

IDEA集成

需要集成Scala插件

在线安装

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIvzSJyB-1575721948435)(assets/1573789318905.png)]

离线安装

注意版本

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qtET4N0-1575721948436)(assets/1573789546245.png)]

四、变量

表达式

scala> 1+1
res0: Int = 2

scala> 1+2
res1: Int = 3

scala> 1*10
res2: Int = 10

声明变量

语法: var 变量名:类型 = 值

注意: scala中值类型Byte、Short、Int、Long、Float、Double、Char、Boolean【对象】

  • 所有的值类型都归属于scala.*
  • String类型延续了java.lang.String
  • 值类型的默认类型:整数(Int)、小数(Double)
  • 类型支持自动推断
var a:Int = 10
var b:String = "Hello"
var c:Boolean = false
# 如以下语法:
scala> var a:Int = 10
a: Int = 10

scala> var b:java.lang.String = "aa"
b: String = aa

scala> var d = 1
d: Int = 1

scala> var e = 1.0
e: Double = 1.0
// ...

声明常量

常量只允许赋值一次

语法:val 常量名:类型= 值

注意:

  • 创建对象时,建议使用常量val
scala> val f = 10
f: Int = 10

# 常量只能赋值一次
scala> f = 11
<console>:12: error: reassignment to val
       f = 11

类型转换

通俗理解: 自动类型提升

规则:Byte ---> Short ---> Int ---> Long ---> Float ---> Double

char

值类型可以按照下面的方向进行转换:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dRH9J08g-1575721948437)(assets/20191114100636493.png)]

scala> var a : Byte = 10
a: Byte = 10

scala> var b: Short = a
b: Short = 10

scala> var c : Int = b
c: Int = 10

scala> var d :Short = c
<console>:12: error: type mismatch;
 found   : Int
 required: Short
       var d :Short = c
                      ^

scala> var e : Int = 'A'
e: Int = 65

scala> var e : Int = 'a'
e: Int = 97

scala> var f : Double = e
f: Double = 97.0

运算符

  • 赋值运算符: =
  • 关系运算符: > 、< 、<= 、>= 、!=、 ==
  • 逻辑运算符:&& 、 || 、!
  • 位运算符:& 、| 、~ 、^、<<、 >>、 >>>
  • 算术运算符:+、-、*、/、%

注意:scala没有++--语法,但是依然保留了+=-=

//---------------------------------------
// 关系运算符
scala> 10 > 8
res3: Boolean = true

scala> 10 <= 10
res4: Boolean = true

scala> 10 != 9
res5: Boolean = true

scala> 10 ==7
res6: Boolean = false
//---------------------------------------

//---------------------------------------
// 逻辑运算符
scala>  1==1 && 3>2
res7: Boolean = true

scala>  1==1 && 3<2
res8: Boolean = false

scala>  1==1 || 3<2
res9: Boolean = true
//---------------------------------------

//---------------------------------------
// 位运算符
// 按照值得二进制进行位计算(效率高)
// 1 & 2 = 0
// 1 | 2 = 3
// 1: 00000001
// 2: 00000010
// 注意: & 二进制位同时出现1 结果为1 不同为0
//       | 二进制位有一个为1 结果为1 不同为0
//       ^
//       ~
//       <<
//       >> 和 >>>
scala> 1&2
res12: Int = 0

scala> 1|2
res13: Int = 3

//---------------------------------------
scala> 1+1
res0: Int = 2

// scala是纯面向对象的  运算符都是值类型中方法
scala> 1.+(1)
res1: Int = 2

Scala类型层次结构【重点】

在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zPfgUfv-1575721948438)(assets/20191114100738761.png)]

  • Any是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如equalshashCodetoStringAny有两个直接子类:AnyValAnyRef
  • AnyVal代表值类型。有9个预定义的非空的值类型分别是:DoubleFloatLongIntShortByteCharUnitBooleanUnit(类似于java中的void)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()。所有的函数必须有返回,所以说有时候Unit也是有用的返回类型。
  • AnyRef代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef的子类型。如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object
  • Nothing是所有类型的子类型,也称为底部类型。没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
  • Null是所有引用类型的子类型(即AnyRef的任意子类型)。它有一个单例值由关键字null所定义。Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null的替代方案。

这里有一个例子,说明了字符串、整型、布尔值和函数都是对象,这一点和其他对象一样:

val list: List[Any] = List(
  "a string",
  732,  // an integer
  'c',  // a character
  true, // a boolean value
  () => "an anonymous function returning a string"
)

list.foreach(element => println(element))

这里定义了一个类型List<Any>的变量list。这个列表里由多种类型进行初始化,但是它们都是scala.Any的实例,所以可以把它们加入到列表中。

下面是程序的输出:

a string
732
c
true
<function>

注意:

​ 在scala的命令窗口,如果想写入多行代码,可以使用:paste模式,多行代码写入完成后,按ctrl+D键退出执行

五、分支语句

类似于Java的分支

if语句

# 语法:
if(布尔条件){
    // 内容
}

//-------------------------------------
scala> val age = 18
age: Int = 18

scala> :paste
// Entering paste mode (ctrl-D to finish)

if(age > 16){
  println("青年")
}

// Exiting paste mode, now interpreting.

青年

if…else语句

# 语法:
if(布尔条件){
    //为true执行
}else{
    //为false执行
}

//--------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)

if(age>16){
	println("青年")
}else{
	println("未成年")
}
// Exiting paste mode, now interpreting.

未成年

if … else if …else语句

#语法:
if(布尔条件1){
    // true 执行
}else if(布尔条件2){
    // true 执行
}else{
    // 以上条件不匹配 执行
}

//------------------------------------
val b = 
if(age>18){
    println("青年")
}else if(age>10){
    println("少年")
}else{
    println("儿童")
}

//------------------------------------
val d = 
if(age>18){
    age + 1
}else if(age>10){
    age + 2
}else{
    age + 3
}
// val d:Int = 16

//------------------------------------
val f = 
if(age>18)age + 1
else if(age>10) age + 2
else age + 3

// val f:Int = 16

总结:

  • scala中所有的表达式都是有值得,如果表达式的最后一行内容为打印语句,则表达式的值为() Unit类型
  • scala中代码块的最后一行内容为表达式的值
  • scala中代码块中有多行内容时,必须使用大括号{}包裹起来,如果只有一行内容,{}可以省略

模式匹配可以代替switch语句

注意:

  • scala中并没有提供switch语句,但是提供了一个功能远超于switch语句的语法,叫做模式匹配
# 语法:

变量名 match{
    case 值 => 处理内容
    case 值 => 处理内容
    case _ => 处理内容
}

注意:
1. `_` 类似于default
2. 模式匹配不存在匹配穿透问题

//---------------------------------------------------------------
val num = 1

num match{
    case 1 => println(1)
    case 2 => println(2)
    case _ => println("其它")
}

六、循环

while

# 语法:
while(循环条件){
    // 循环体
}

// 1~100数值之和
var n = 1
var sum = 0
while(n <= 100){
    sum += n
    n += 1
}
//------------------------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)

var n = 1
var sum = 0
while(n <= 100){
    sum += n
    n += 1
}

// Exiting paste mode, now interpreting.

n: Int = 101
sum: Int = 5050

do…while…

# 语法:
do{
    // 循环体
}while(循环条件)

// 1~100数值之和
var n = 1
var sum = 0
do{
    sum += n
    n += 1
}while(n <= 100)

//--------------------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)

var n = 1
var sum = 0
do{
    sum += n
    n += 1
}while(n <= 100)

// Exiting paste mode, now interpreting.

n: Int = 101
sum: Int = 5050

for

# java for
for(int i = 0; i<=100;i++){
    // 循环体
}

# scala for
for(i <- 0 to 100){
    println(i)
}

//-----------------------------------------
var sum = 0
for(i <- 0 to 100){  // 数值区间Range(0~100),包含起始和结束数值
    sum += i
}
println(sum)

var sum = 0
for(i <- 0 until 100){  // 数值区间Range(0~99),包含起始和不包含结束数值
    sum += i
}
println(sum)
//-----------------------------------------

// 通过for循环实现九九乘法表
for(m <- 1 to 9){
    for(n <- 1 to m){
        print(n+"*"+m+"="+(m*n) +"\t")
    }
    println()
}

1*1=1
1*2=2   2*2=4
1*3=3   2*3=6   3*3=9
1*4=4   2*4=8   3*4=12  4*4=16
1*5=5   2*5=10  3*5=15  4*5=20  5*5=25
1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81

嵌套for循环

嵌套for循环的简化写法

# 嵌套for语法
# 多重循环在for的()中用`;`分割
for(m <- 1 to 9; n <- 1 to m){
    if( m != n){
        print(n+"*"+m+"="+(m*n) +"\t")
    }else{
        println(n+"*"+m+"="+(m*n) +"\t")
    }
}

迭代的步长

scala> 1 to 10 by 2
res24: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

scala> 1 to 10 by 3
res25: scala.collection.immutable.Range = Range(1, 4, 7, 10)

// 统计 1 ~ 100 偶数之和
var sum = 0
for( n <- 0 to 100 by 2){
    sum += n
}
println(sum) // 2550

if守卫

// 通过循环计算 1~10 奇数之和
var sum = 0
for(n <- 1 to 10){
    if( n % 2 != 0){
        sum += n
    }
}

// if守卫简化实现
var sum = 0
for(n <- 1 to 10 if n % 2 != 0){
    sum += n
}  // 25

// n 必须是奇数 并且n不能是5
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 && n != 5){ 
    sum += n
}  // 20

// n 是奇数 或者n可以使6
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 || n == 6){ 
    sum += n
}  // 31

break

Java break: 表示结束循环

注意:scala中没有breakcontinue关键字

import scala.util.control.Breaks._

breakable {
  for (n <- 1 to 10) {
    if (n == 5) break()  // 在scala中如果是一个无参的方法,可以省却(),直接写方法名
    println(n)
  }
}

yield

for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合

  • 首先会将yield 表达式的值会被加入存放到一个缓冲区中
  • 在遍历结束之后,会返回一个基于缓冲区的新集合,类型为Vector
  • 新集合的元素的类型和遍历集合的类型是一致的
val newColle = for(n <- 1 to 10 if n % 2 == 1 && n != 5) yield n*3
//  1 3 7 9 
//  newColle[3 9 21 27]

//--------------------------------------------------------------------
scala> val newColle = for(n <- 1 to 10 if n % 2 == 1 && n != 5) yield n*3
newColle: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 9, 21, 27)

任务:熟悉scala的基本语法,corejava前三个章节的联系题(scala重构)

七、函数

函数是一段特定功能的代码块

三要素: 函数名、参数列表、返回值

public static 返回值类型 m1(参数列表){
    // 函数体
    return xxx;
}

函数声明

语法:

def 函数名(参数列表):返回值类型 = {
    // 函数体
}

// 如:
def main(args:Array[String]):Unit ={
    // 主函数内容
}
package function

object Function01 {
  // main 快速生成main函数
  def main(args: Array[String]): Unit = {
    // 如何调用普通函数
    println(sum(1, 2))
    println(sum2(1, 2))
    println(sum3(1, 2)) // sum ~ 3   sum2 ~ "3"  sum3 ~ 3  ()
    println(sum5(1, 2)) // sum ~ 3   sum2 ~ "3"  sum3 ~ 3  ()  ()
  }

  // 声明普通函数
  def sum(x: Int, y: Int): Int = {
    val total = x + y // 自动将代码块的最后一行内容作为返回值
    total
  }

  // 声明普通函数【简化】
  def sum2(x: Int, y: Int) = { // 省略返回值类型 `:Int`  自动推导返回值的类型
    val total = x + y // 自动将代码块的最后一行内容作为返回值
    total + ""
  }

  // 无返回值的函数  Unit类型的值为()
  def sum3(x: Int, y: Int): Unit = {
    println(x + y)
  }


  // 无返回值的函数  Unit类型的值为() 【简化写法】  省略`: Unit =`
  def sum4(x: Int, y: Int) {
    println(x + y)
  }


  // 声明普通函数
  def sum5(x: Int, y: Int){
    val total = x + y // 自动将代码块的最后一行内容作为返回值
    total
  }
}

总结:

  • 有返回值的函数,在声明时可以省略:返回值类型,scala自动根据类型推导返回值类型
  • 无返回值的函数,在声明时可以省略:返回值类型=, scala会自动认为是无返回值的函数

变长参数

在java中如果声明可变长参数public static void m1(String ... args)

在scala中如果声明可变长参数def 函数名(args:Int*):返回值类型 ={}

package function

/**
  * 可变长参数
  */
object Function02 {

  def main(args: Array[String]): Unit = {
    println(m1(1, 2, 3, 4, 5)) // 15
    println(m1(1, 2, 3, 4, 5, 6, 7)) // 28
    println(m2(1, 1,2, 3, 4, 5, 6, 7)) // 29
  }

  // 声明一个Int类型的可变长参数 如:计算参数列表的整数之和
  def m1(args: Int*): Int = { // 数组
    var total = 0
    for (n <- args) {
      total += n
    }
    total
  }

  // 注意: 在定义可变长参数时,可变长参数需要放置在函数的参数列表之后
  def m2(init: Int, args: Int*) = {
    var default = init
    for (n <- args) {
      default += n
    }
    default
  }
}

参数默认值

scala的函数式可以有默认值的,在使用时

  • 如果调用传值,使用传值进行计算
  • 如果调用没有传值,则使用默认值计算
def sum(x: Int, y: Int, z: Int = 10): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
    x+y+z
}

带名参数

在调用函数时,给指定参数名的参数赋值

println(sum2(x = 1, z = 2)) // 13 【使用带名参数】
println(sum2(z = 2, x = 1)) // 13 【使用带名参数】 注意:带名参数使用时与函数的参数列表的顺序无关

def sum2(x: Int, y: Int = 10, z: Int): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
    x + y + z
}

Lazy值(懒加载)

在scala中变量或者常量可以被声明为懒加载的值,值在第一次使用时,才会进行初始化

package function
import scala.io.Source._
/**
  * 懒加载
  */
object Function04 {
  def main(args: Array[String]): Unit = {
    // 语法 lazy 变量或者常量的声明

    // 立即进行初始化 ERROR
    // val source = fromFile("D://abc.txt")

    // 懒加载的值
    lazy val source2 = fromFile("D://abc.txt")

    // 使用时,懒加载的值该初始化了,ERROR
    source2.mkString
  }
}

递归函数

类似于JAVA的递归函数

// 计算1~10的阶乘
package function

/**
  * 递归函数(求1~n阶乘)
  */
object Function05 {
  def main(args: Array[String]): Unit = {

    println(m1(3)) // 6
    println(m2(5))
  }

  // 求1~n的整数之和
  def m1(n: Int): Int = {
    var total = 0
    if (n > 0) {
      total += n + m1(n - 1)
    }
    total
  }

  // 第一次递归: n <= 0   total = 0
  // 第二次递归: n = 1    total = 0 + 1
  // 第三次递归: n = 2    total = 0 + 1 + 2
  // 第四次递归: n = 3   total = 0 + 1 + 2 + 3 = 6


  def m2(n: Int): Int = {
    var total = 1
    if (n >= 1) {
      total *= n * m2(n - 1)  // 1*2*3*4*5 = 120
    }
    total
  }

  // 第一次递归:
}

匿名函数【重点】

没有函数名的函数

语法:参数列名 => 函数体

注意: 内置了23个匿名函数对象Function0~Function22, 匿名函数的背后对应的是一个函数对象

(x:Int,y:Int) => x+y
package function

/**
  * 匿名函数
  */
object Function06 {
  def main(args: Array[String]): Unit = {

    // 匿名函数声明
    val f2: Function2[Int, Int, String] = (x: Int, y: Int) => x + y + " "

    // 匿名函数使用
    println(f2(1, 2))
    println("-------------------------------------")
    val f1: Function1[Int, Unit] = (n: Int) => println(n)

    val list = List(1, 2, 3, 4, 5)

    // 将函数对象作为方法的参数
    list.foreach(f1)

    println("-------------------------------------")

    list
      .filter((n: Int) => n != 3) // 保留集合中符合条件的数据
      .foreach((n: Int) => println(n)) // 1 2 4 5
  }
}

柯里化(Currying)

定义:

​ 柯里化函数指将接受多个参数的函数,改造为接受一个参数的函数,并且返回一个函数对象的过程

package function

/**
  * 柯里化函数的定义
  *
  * 柯里化函数指将接受多个参数的函数,改造为接受一个参数的函数,并且返回一个函数对象的过程
  */
object Function08 {
  def main(args: Array[String]): Unit = {
    println(sum(1, 2)) // 3
    println(sum2(1)) // Function1
    println(sum2(1)(2)) // 结果:3 第一步: sum2(1) <==> (y:Int) => 1+y  第二步:sum2(1)(2) <==> (2) => 1+2

    println(sum3(1)(2)) // 结果:3 第一步: sum2(1) <==> (y:Int) => 1+y  第二步:sum2(1)(2) <==> (2) => 1+2
  }

  // 普通函数
  def sum(x: Int, y: Int) = {
    x + y
  }

  // 柯里化函数
  def sum2(x: Int) = { // 返回值: 函数对象Function1
    (y: Int) => x + y
  }

  // 柯里化函数【简化】   省略了 `返回值类型信息`  将匿名函数的连接符 修改为了 = 即可
  def sum3(x: Int)(y: Int) = {
    x + y
  }

  def sum4(x: Int) = {
    (y: Int) => (z: Int) => x + y + z   // sum4 接受单个参数的普通函数
                                        // sum4 函数体内 匿名函数 (y:Int) => //匿名函数体
                                        // 匿名函数体又是一个匿名函数 (z:Int) => x+ y+z
  }

  def sum5(x:Int)(y:Int)(z:Int) ={
    x+y+z
  }
}

八、数组

数组定义: 数据的集合,在内存中表示为一段连续的存储空间

数组的声明

// java  int[] arr = new int[]{1,2,3,4,5}
//       int [] arr = new int[10]
//       int arr[] = {1,2,3,4,5}

// 语法:  val arr = Array[泛型](元素列表)
// scala数组的声明
val arr = Array[Int](1, 2, 3, 4, 5)

数组的使用

  • 长度(length): 1~n
  • 下标(index) : 0~ n-1
// 遍历
package array

object Array01 {
  def main(args: Array[String]): Unit = {
    // scala数组的声明
    val arr = Array[Int](1, 2, 3, 4, 5)

    // 遍历方法1
    for(n <- 0 until arr.length){
      println(arr(n))  // 注意: 获取数组中元素使用的是 数组名(下标)
    }

    println("-----------------------------------")

    // 遍历方法2
    for(n <- arr){
      println(n)
    }

    // 修改数组中的元素
    arr(3) = 10

    println("-----------------------------------")
    // 遍历方法3
    for(n <- arr.indices){  // indices 获取到的是数组的下标数值区间
      println(arr(n))
    }
  }
}

数组的排序

// 冒泡排序
package array

/**
  * 冒泡排序
  *
  * Array(5,10,2,1,6,7)
  *
  *   第一次:
  *      5 10 2 1 6 7
  *      5 2 10 1 6 7
  *      5 2 1 10 6 7
  *      5 2 1 6 10 6
  *      5 2 1 6 7 10
  *   第二次:
  *     ...
  */
object Array02 {
  def main(args: Array[String]): Unit = {
    val arr = Array(5,10,2,1,6,7)

//    for(m <- 0 until arr.length){
//      for(n <- 0 until arr.length - m -1){
//        if(arr(n) > arr(n+1)){
//          val tmp = arr(n)
//          arr(n) = arr(n+1)
//          arr(n+1) = tmp
//        }
//      }
//    }

    // 改造代码【嵌套 + if守卫】
    for(m <- 0 until arr.length; n <- 0 until arr.length - m -1 if arr(n) > arr(n+1)){
        val tmp = arr(n)
        arr(n) = arr(n+1)
        arr(n+1) = tmp
    }

    for( m <- arr){
      println(m)
    }
  }
}

可变数组ArrayBuffer

Array 定长数组,一旦声明 数据长度就固定不可变

ArrayBuffer 可变数组,类似于集合,数组会自动扩容

package array

import scala.collection.mutable.ArrayBuffer

/**
  * 可变数组
  */
object Array03 {
  def main(args: Array[String]): Unit = {

    val ab = ArrayBuffer(1, 2, 3, 4, 5)

    // 可变数组添加元素
    ab += 6

    ab.+=(7)

    for(n <- ab){
      println(n)
    }
  }
}

Array和ArrayBuffer相互转换

// arrayBuffer ---> array  可变到定长的转换
val arr = ab.toArray

// array ---> arrayBuffer 定长到可变长的转换
arr.toBuffer

多维数组

多维: 数组的元素又是数组

package array

/**
  * 二维数组
  */
object Array04 {
  def main(args: Array[String]): Unit = {
    val arr = Array.ofDim[Int](10, 4) // 类似于10行4列的二维表

    // 操作
    arr(1)(2) = 2

    // 获取
    println(arr(1)(2))

    for (m <- 0 to arr.length - 1) {
      for (n <- 0 to arr(m).length - 1) {
        print(arr(m)(n) + "\t")
      }
      println()
    }
  }
}

练习

2:00~2:30

选择排序

算法案列:移除第一个负数后的所有数

  • 测试数据val a1=Array(1,2,3,4,5,-5,-4,-3)
  • 预期结果val a2=Array(1,2,3,4,5,-5)

九、类

类(class):对象的模板,成员(有什么)和方法(做什么)

简单类

语法: class 类名{ // 成员或者方法 }

// 声明一个Animals
class Animals{}

// 创建对象
val animals:Animals = new Animals()
或
val animals:Animals = new Animals

//---------------------------------------
classes.Animals@47f37ef1
classes.Animals@5a01ccaa

注意:

  • 类中提供一个默认的无参的主构造器
  • 如果创建对象时调用是无参的构造方法,()可以省略

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PKOdklZi-1575721948440)(assets/1574062256404.png)]

成员和方法

成员

注意:

  • scala中成员全部都是私有的(private
  • scala中成员的getter方法名成员名()
  • scala中成员的setter方法名成员名_$eq(参数)
// 如何声明成员,语法:
// ① 声明变量成员, 都是私有的成员,scala自动为成员提供公开的setter【id_$eq | name_$eq】/getter【id() | name()】方法
var id: Int = 0 // 必须指定初始值
var name: String = ""


// ② 声明常量成员,依然是私有的成员,scala自动为成员提供公开的getter【sex()】方法
val sex: Boolean = false

// 变化一 private
// ③ 成员private修饰的变量,依然是私有的成员,scala自动为成员提供私有的getter/setter方法
private var birthday = new java.util.Date()

// 变化二 private
// ④  成员private修饰的常量,依然是私有的成员,scala自动为成员提供私有的getter方法
private val address = ""

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vb8IbRJB-1575721948441)(assets/1574064931000.png)]

方法

语法: 和函数的语法一样

package classes

/**
  * scala方法
  */
object ColorTest {
  def main(args: Array[String]): Unit = {
    val red: Red = new Red
    println(red.display())
    println(red)
  }
}

class Red {

  var name: String = "red"

  // 方法
  def display(): String = {
    this.name
  }

  override def toString: String ="红色"
}

手动生成getter/setter方法

语法:

  • getter方法 ----> def 区分成员名() = 成员值

  • setter方法 ----> def 区分成员名_(形参) {成员值 = 形参值}

package classes

object PersonTest {
  def main(args: Array[String]): Unit = {
    val person = new Person
    person.setName_("zs")

    println(person.getName)
  }
}

class Person {
  // 自定义name成员的getter和setter方法
  var name: String = ""

  // 自定义getter
  def getName(): String = this.name

  // 自定义setter方法
  def setName_(newName: String): Unit = this.name = newName
}

@BeanProperty

此注解加载成员的前面,scala编译时,会自动生产java版本和scala版本的getter/setter方法

package classes

import scala.beans.BeanProperty

object CourseTest {
  def main(args: Array[String]): Unit = {
    val chinese = new Chinese

    chinese.getCourseName
    chinese.setCourseName("文言文")

    chinese.courseName = "四书五经" // scala风格setter方法
    val courseName = chinese.courseName
    println(courseName)
  }
}

class Chinese {

  @BeanProperty var courseName: String = "中文"
}

辅助构造器

辅助构造器,类似于java重载的构造方法

注意:scala的一个类中,可以有一个主构造器和若干个辅助构造器

package classes

object FishTest {
  def main(args: Array[String]): Unit = {
    val fish1 = new Fish() // 调用无参数的主构造器
    val fish2 = new Fish("金鱼") // 调用无参数的主构造器
    val fish3 = new Fish("金鱼", "黄色") // 调用无参数的主构造器

    println(fish1)
    println(fish2)
    println(fish3)

    //----------------------------
    /*
    金鱼
    金鱼	黄色
    */
    //----------------------------
  }
}

// scala默认提供一个无参的主构造器
class Fish {

  var name: String = ""
  var color: String = ""

  // 辅助的构造器
  // 注意:辅助构造器必须以调用一个主构造器或者辅助构造器开始
  def this(name: String) {
    this()
    this.name = name
  }

  def this(name: String, color: String) {
    this(name)
    this.color = color
  }

  override def toString: String = this.name + "\t" + this.color
}

注意事项:

  • 辅助构造器,使用的关键字this
  • 辅助构造器内,第一行必须调用主构造器或者辅助构造器

主构造器

  • 声明,主构造器的参数列表直接写在类名之后:class Fish(var name:String,var color:String){}
  • 主构造器的方法体,是整个类,也就是类的{}的所有内容,注意主构造器方法体中的成员和方法仅仅是声明,并不会执行
package classes

object PandaTest {
  def main(args: Array[String]): Unit = {
    val p1 = new Panda("小黑","熊猫色")
    println()
    val p2 = new Panda("小黑","熊猫色")
  }
}

// 主构造器
class Panda(var name: String, var color: String) {

  var age: Int = 0

  // 方法体
  println("before")

  def sayHello(str: String): String = {
    str + name
  }
  println("after")
}

//class Panda{
//  private String name;
//  private String color;
//  private Int age;
//  public Panda(String name,String color){
//    this.name = name;
//    this.color = color;
//  }
//}

十、对象(object)

代替java中static语法的,因为scala没有static,如果需要在scala的源文件中定义静态的成员和方法,使用object类型

单例对象

不管创建多少次,只存在一个对象实例

总结:

  • scala中一个类,类型为object,此类即为单例对象
  • 类体中成员和方法,都是静态成员【所有类共享的】
package objectes

/**
  * scala中一个类,类型为object,此类即为单例对象
  */
object SingletonObject {

  // 静态属性
  var id: Int = 0

  // 静态方法
  def incrementId(): Int = {
    this.id = this.id +1
    this.id
  }

  def main(args: Array[String]): Unit = {
    val s1 = SingletonObject
    val s2 = SingletonObject

    val s3 = new SingletonObject()
    val s4 = new SingletonObject()

    println(s1 == s2) //true【单例】
    println(s3 == s4) //false【多例】

    val id1 = s1.incrementId()
    val id2 = s2.incrementId()
    println(id1+"\t"+id2)
  }
}

class SingletonObject {

}

伴生对象&伴生类

在一个scala的源文件中,class的名字和object名字一样,class类就被称为伴生类,object类就称为伴生对象

package objectes

/**
  * 伴生类&伴生对象
  *
  * 注意:伴生类可以访问伴生对象的私有成员
  *
  *   java中: 普通方法能不能调用静态方法或者成员 【可以】
  */
class Person {

  def sayHi(): String = {
    "Hi:" + Person.name + "\t" + Person.id
  }
}

object Person {

  val id: Int = 0

  private val name: String = "zs"


  def main(args: Array[String]): Unit = {
    val person = new Person
    println(person.sayHi())
  }
}

List(1,2,3)类型

apply & unapply

apply 方法是object类型中的一个方法,通常作为工厂方法创建伴生类的对象【简化方式】

结论:** 调用的伴生对象中的apply方法,用以创建伴生类**

package objectes

class Cat(var name: String, var color: String) {

}

object Cat {

  // apply方法创建伴生类
  def apply(name: String, color: String): Cat = new Cat(name, color)

  def main(args: Array[String]): Unit = {
    // 创建Cat对象
    val cat = new Cat("小花", "white")

    // apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
    // 创建伴生类对象时【简化写法】
    val cat2 = Cat("小黑","black")  // 等价于Cat.apply("小黑","black")
    val cat3 = Cat("小黑","black")  // 等价于Cat.apply("小黑","black")
    println(cat2 == cat3)
  }
}

unapply: 伴生对象中的方法,作用和apply方法相反,接受一个伴生类对象,返回伴生类对象的成员

package objectes

class Cat(var name: String, var color: String) {

}

object Cat {

  // apply方法创建伴生类
  def apply(name: String, color: String): Cat = new Cat(name, color)

  // 将伴生类对象 拆箱为成员
  // Option 【避免出现空指针异常,有值 Some  无值 None】
  //   - None
  //   - Some
  def unapply(arg: Cat): Option[(String, String)] = {
    if (arg == null) None
    else {
      Some((arg.name, arg.color)) //(name,color) scala元组,可以存放任意类型的list集合 类型与java中的ArrayList[Object]
    }
  }

  def main(args: Array[String]): Unit = {
    // 创建Cat对象
    val cat = new Cat("小花", "white")

    // apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
    // 创建伴生类对象时【简化写法】
    val cat2 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
    val cat3 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
    println(cat2 == cat3)

    // new Array[Int](10)
    // Array(1,2,3,4)
    // List(1,2,3,4,5)  调用的伴生对象中的apply方法,用以创建伴生类

    // ================================================
    // unapply方法应用
    val Cat((name, color)) = cat3   // Cat.unapply(cat3) = Some(cat3.name,cat3.color)
                                    // val name = cat3.name
                                    // val color = cat3.color
    println(name + "\t" + color)
    // ================================================
  }
}

十一、继承

Java中的继承,子类继承父类等价于继承了父类成员和方法,使用的关键字extends,单继承

基本语法

package extendses

class Animals {
  // 私有成员,公开的getter/setter方法,
  var name: String = ""

  def sayHi(): String = {
    "Hello:" + name
  }
}

class Dog extends Animals {
}

object Dog {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.name = "小黑"  // 父类的setter
    println(dog.sayHi())  // 父类的sayHi
  }
}

注意:

  • scala的继承同样使用的是extends
  • scala的子类同样可以继承父类中的成员和方法

方法覆盖

覆盖:父类定义的方法无法子类需求时,在子类中定义相同的方法,在调用时,以子类优先

关键字override,不可省略

package extendses

class Person {

  def run(): Unit = {
    println("人在奔跑!")
  }
}

class SmallStudent extends Person {

  // 父类方法的覆盖 `override`
  override def run(): Unit = println("小学生在学走路!")
}

object SmallStudent {
  def main(args: Array[String]): Unit = {
    val student = new SmallStudent
    student.run()  // 小学生在学走路!
  }
}

类型检查和转换

java: isInstanceof if(dog isInstanceof Animals)

scala :

  • 判断类型是否兼容:isInstanceOf[类型]

  • 类型转换语法: asInstanceOf[另外一种类型]

val bool = student.isInstanceOf[Person]
println(bool) // true

val person = new Person
val bool2 = person.isInstanceOf[SmallStudent]
println(bool2)  // false

//-------------------------------------------
// 将小学生的类型 赋值父类型
val person2: Person = student.asInstanceOf[Person]

// 因为person2的真实类型是SmallStudent
val student2 = person2.asInstanceOf[SmallStudent]

调用父类有参构造

结论:

  • 如果调用父类的有参构造方法,需要在extends 父类(父类的有参构造)
  • 子类的主构造器中,必须有父类的成员,并且不需要声明为val或者var
package extendses

// color父类没有无参的主构造器,只有一个有一个参数的主构造器
// scala中没有`Super()`语法,子类必须得调用父类的有参构造
class Color(var name: String) {
  println("父类")
}


class Red(name: String) extends Color(name: String) {
  println("子类")
}

object Red {
  def main(args: Array[String]): Unit = {
    val red = new Red("红色")
    println(red.name)
  }
}


//class Color{
//  String name;
//  public Color(String name){
//    this.name = name;
//  }
//}
//
//class Red extends Color{
//
//  public Red(){
//    super("red")
//  }
//}

重写字段

结论:

  • 如果子类需要覆盖父类中的变量(var),子类中不需要var关键字和变量的类型
  • 如果子类需要覆盖父类中的常量(val),子类必须在父类常量的基础之上加上override关键字
  • 如果父类中的私有成员(private),子类是无法覆盖,原因是:父类中属性和getter/setter方法都是私有的
package extendses

class A {
  var id: Int = 0
  val name: String = ""
  private var sex: Boolean = false // getter/setter私有的方法
}


class B extends A {
  // 子类覆盖父类成员id/name

  // 覆盖重写变量 id, 注意:省略了变量的关键字`var`和变量的类型
  id = 10

  // 覆盖重写常量 name
  override val name: String = "BB"

  private var sex: Boolean = true // 并不是继承,子类中特有的成员
}

object B {
  def apply(): B = new B()

  def main(args: Array[String]): Unit = {
    val b = B()
    println(b.id + "\t" + b.name + "\t" + b.sex)  //10	BB	true
  }
}

抽象类

类似于java的抽象类,使用的关键字依然是abstract

  • 抽象类,可能有抽象成员和普通成员
  • 子类继承抽象类,必须得实现抽象类的声明方法,如果未实现,子类依然是一个抽象类
  • 抽象类只能声明引用而不能创建对象
  • 在实现抽象类的抽象方式或者属性时,依然使用的是关键字override
package extendses

// 抽象类
abstract class Person2 {
  // 抽象字段
  val name: String

  // 抽象方法
  def playGame(): String
}

object Person2Test {
  def main(args: Array[String]): Unit = {
    //1. 抽象类只能声明引用而不能创建对象

    val person1:Person2 = new SmallStudent2
    println(person1.name +"\t" +person1.playGame)
    
    val person2:Person2 = new HighStudent2
    println(person2.name +"\t" +person2.playGame)
  }
}

class SmallStudent2 extends Person2 {

  override val name: String = "小学生"

  override def playGame(): String = "王者荣耀"
}

abstract class MiddleStudent2 extends Person2 {

  override def playGame(): String = "英雄联盟"
}

class HighStudent2 extends Person2 {

  override def playGame(): String = "绝地求生"

  override val name: String = "高中生"
}

匿名子类

类似于java中的匿名内部类

abstract class A2 {
  def m1(): Unit
}

class B2 extends A2 {
  def m1(): Unit = {
    // 方法体
  }
}

// 匿名子类
val a2: A2 = new A2 {
    override def m1(): Unit = println("m1")
}
a2.m1()

包可见性(访问修饰符)

Java访问修饰符: public、default(默认)、protected、private

Scala访问修饰符:default(public)、protected、private

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mlkqolg9-1575721948442)(assets/20171013163614831.png)]

package extendses

class AA {
  // 默认访问修饰符 public:全部
  var name: String = ""

  // protected范围:本类、伴生对象、子类
  protected var sex: Boolean = false

  // private范围:本类、伴生对象
  private var address: String = ""

}

object AA {
  def main(args: Array[String]): Unit = {
    val aa = new AA
    aa.address // private 伴生对象ok
    aa.sex // protected 伴生对象ok
  }
}

class BB extends AA {
  def m1(): Unit = {
    sex // protected 子类 OK
    // address // private 子类 ERROR
  }
}


class DD{
  def m2(): Unit ={
    // new AA().sex() //error
    new AA().name //ok
  }
}

十二、特质(trait)

特质: 类似于Java中的接口

语法

  • 特质关键词trait

  • 特质可以同时用于抽象方法和具体方法

  • 无父类 实现特质extends trait1 with trait2 with trait3 ...

  • 有父类 实现特质extends 父类 with 特质1 with 特质2 ...

当做接口的特质

package traites

/**
  * 等价于Java接口
  *   1. 公开静态常量
  *   2. 公开抽象方法
  *   3. JDK8之后,接口中也可以由具体的方法
  */
trait Person {

  // 特质中的属性
  var name: String

  // 特质中的一个抽象方法
  def run(): String
}

class Father

class SmallStudent extends Person {

  override var name: String = "小学生"

  override def run(): String = name + "在走路!"
}

class MiddleStudent extends Father with Person{
  override var name: String = "中学生"

  override def run(): String = name +"在奔跑!"
}


object PersonTest {
  def main(args: Array[String]): Unit = {
    val student = new SmallStudent
    student.name = "小屁孩"
    println(student.run())
  }
}

带有具体实现的特质

注意:

scala的特质类中可以有具体实现的,类似于JDK8的接口

package traites

trait Animals {
  // 注意:特质中既可以有抽象成员 也可以有具体成员
  var name: String = "小动物"

  def sleep(): Unit = {
    println(name + ":在睡觉!")
  }

  def eat(): String
}

class Dog extends Animals {
  override def eat(): String = "吃饭!"

  override def sleep(): Unit = println(name +":sleeping")
}

object Dog {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.eat()
    dog.sleep()
    dog.name ="xx"
  }
}

注意:让特质混有具体行为有一个弊端. 当特质改变时, 所有混入该特质的类都必须重新编译

带有特质的对象 动态混入(mixin) [ 重点]

动态混入(mixin):指在创建单个对象时,可以在不破换对象声明的前提下,扩展对象的功能

在构造单个对象时, 你可以为它添加特质

  • 特质可以将对象原本没有的方法与字段加入对象中, 功能扩展
  • 无需修改类声明定义,扩展类的功能,灵活简便
  • 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
package traites.mixin

trait Animals {
  println("animals")
  def eat(): Unit
}


trait Person {  // 都一个无参的主构造器
  println("person")
  def playGame(): Unit
}

trait Student extends Animals {
  println("student")
  // def study(): Unit
  // 修改:
  def study(): Unit

  override def eat(): Unit = println("学生吃的肉和菜")
}

// 传统写法:在增强 SmallStudent 功能混入两个Trait
//class SmallStudent extends Person with Student {
//  override def playGame(): Unit = ???
//
//  override def study(): Unit = ???
//
//  override def eat(): Unit = ???
//}

class SmallStudent extends Animals { // 在不破坏类的声明要求下,扩展功能(吃、玩游戏、学习能力)
  println("smallStudent")

  override def eat(): Unit = println("小学生吃零食")
}

object SmallStudent {
  def main(args: Array[String]): Unit = {
    // 注意:特殊语法,可以在创建伴生类时混入特质[抽象类],增强伴生类的功能,这样的语法就称为动态混入(mixin)
    // 请问在有动态混入语法时:对象创建的顺序 从左到右进行初始化
    /*
      smallStudent
      person
      animals
      student
     */
    val smallStudent = new SmallStudent with Person with Student {
      override def playGame(): Unit = println("打游戏")
      override def study(): Unit = println("音乐和美术")
    }
    smallStudent.playGame()
    smallStudent.study()
    // 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
    smallStudent.eat()   // Person$eat 学生吃的肉和菜
  }
}

this别名

给当前类(this)起一个别名(可以随便定制,建议self

看scala的源码的话很发现很多源码开头都有一句:self => 这句相当于给this起了一个别名为selfself不是关键字,可以用除了this外的任何名字命名(除关键字)。就下面的代码,在Student内部,可以用this指代当前对象,也可以用self指代,两者是等价的。

package traites

class AA {
  // this起一个别名
  self =>

  var id: Int = 0

  def getId(): Boolean = {
    // self.id
    this.id == self.id
  }
}

object AA {
  def main(args: Array[String]): Unit = {
    val aa = new AA
    val bool = aa.getId()
    println(bool)  //true
  }
}

//-------------------------------------------------------
class Outer {
  outer =>
  val v1 = "here"

  class Inner {
    inner =>
    val v1 = "-------------"
    println(outer.v1) // 用outer表示外部类,相当于Outer.this
    println(inner.v1)
  }
}
object Outer {
  def main(args: Array[String]): Unit = {
    val outer = new Outer()
    val inner = new outer.Inner

    /**
      * here
      * -------------
      */
  }
}

self-type(自类型)

强制混入

package traites

trait Cat {
  val name: String
}


trait SmallCat{
  // 自类型:强制混入,表示当前特质类必须混入类型Cat
  self:Cat =>

  def sayHi():String = {
    self.name // 必须混入Cat
  }
}

class SmallFamilyCat extends SmallCat with Cat{
  override val name: String = "小花花"
}

object SmallFamilyCat{
  def main(args: Array[String]): Unit = {
    val cat = new SmallFamilyCat
    val result = cat.sayHi()
    println(result)
  }
}

十三、高阶函数

匿名函数

传统写法:def sum(x:Int,y:Int):Int = x+y

简化写法:val f2:Function2[Int,Int,Int] = (x:Int,y:Int) => x+y

柯里化函数

传统写法:def sum(x:Int) = (y:Int) => x+y

简化写法:def sum(x:Int)(y:Int) = x+y

函数对象

Scala默认提供了23个函数对象,Function0~Function22

package function

object FunctionObject {
  def main(args: Array[String]): Unit = {
    val f2 = (x: Int, y: Int) => x + y

    // 创建Function2函数对象
    //    val f2:Function2[Int,Int,Int] = new Function2[Int, Int, Int] {
    //      override def apply(v1: Int, v2: Int): Int = v1 + v2
    //    }

    val sum = f2(1, 2)
    println(sum)
  }
}

函数对象作为参数

注意:

​ Scala的函数对象可以作为值进行传递,原因函数也是对象

package function

object HighLevelFunction01 {
  def main(args: Array[String]): Unit = {
    val f2 = (x: Int, y: Int) => x + y
    sum(1,2,f2)
  }

  /**
    * 
    * @param x
    * @param y
    * @param f2 函数对象
    */
  def sum(x: Int, y: Int, f2: (Int, Int) => Int): Unit = {
    println(f2(x,y)) // 3
  }
}

参数(类型)推断

package function

object HighLevelFunction02 {
  def main(args: Array[String]): Unit = {
    val arr = Array(1, 2, 3, 4, 5)

    // arr.foreach((n: Int) => println(n))

    // 匿名函数 省略了参数列表的类型,因为匿名函数类型和数组元素类型一致
    // arr.foreach((n) => println(n))

    // 接受单个参数 () 省略
    // arr.foreach(n => println(n))

    // 接受单个参数 n => 省略
    // arr.foreach(println(_))
    arr.foreach(println)
  }
}

一些有用的高阶函数

scala的函数实现wordcount

package function

object HighLevelFunction03ForWordCount {
  def main(args: Array[String]): Unit = {
    val lines = Array("Hello Hello","Hello Hadoop")

    lines
      .flatMap((line:String) => line.split(" "))  // 将一个内容展开为0~n个内容
      .map((word:String) => (word, 1))  // 将一个类型转换为另外一个类型
      .groupBy(t2 => t2._1)  // 根据元组中的word分组
      .map(t2 => (t2._1,t2._2.length)) 
      .foreach(println)
    
    println("-------------------------------")

    lines
      .flatMap(_.split(" "))
      .map((_,1))
      .groupBy(_._1)
      .map(t2 => (t2._1,t2._2.length))
      .filter(! _._1.equals("Hello"))  // 
      .foreach(println)

    /*
    res4: scala.collection.immutable.Map[String,Array[(String, Int)]] =
    Map(
      Hadoop -> Array((Hadoop,1)),
      Hello -> Array((Hello,1), (Hello,1), (Hello,1)))
     */
  }
}

函数闭包

闭包指在函数体内,可以访问相应作用域内的任何变量,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。

语法:函数对象 + 外部变量 = 闭包

package function

object HighLevelFunction03 {
  def main(args: Array[String]): Unit = {

    var n = 0

    // 函数闭包: 函数对象 + 外部变量 = 闭包
    val f1 = (x: Int) => x + n

    println(f1(1))

    n = 100 // 函数对象闭包后,只保存一个外部变量的引用地址,当外部的变量在发生改变时,会影响到闭包后的结果

    println(f1(2))
  }
}

十四、异常处理

Java异常分类:

| - Throwable
	|-  Error : 系统底层错误,无法解决
	|-  Exception(异常): 
		 |- RuntimeException
		 |- 非 RuntimeException

Java 异常处理方法:

|- 消极处理  throw 抛出异常【动作】  throws 声明可能抛出异常【名词】
|- 积极处理  try...catch...finally...

注意:Scala异常处理类似于Java的异常处理

package exceptiones

/**
  * 测试scala的异常处理
  */
object Exception01 {
  def main(args: Array[String]): Unit = {
    // m1()
    m2()
    println("-----------------------")
  }

  /**
    * 消极处理  throw
    *
    * 1) scala保留了java异常处理类
    * 2) scala不要通过throws声明可能抛出异常
    */
  def m1(): Unit = {
    // throw new RuntimeException
    throw new NullPointerException
    println("---------------")
  }

  /**
    * 积极处理 try ... catch...finally
    */
  def m2(): Unit = {
    try {
      var i: Int = 1 / 0
    } catch {
      // 模式匹配【详解】  匹配的try块中可能抛出异常的类型
      case e1: NullPointerException => println("空指针异常")
      case e2: RuntimeException => println("运行时异常")
      case e3: Exception => println("异常!!!")
      case _ => println("其它错误")
    }
    finally {
      println("finally block")
    }
  }
}

十五、隐式转换【重点】

隐式转换函数(implicit conversion function)指的是以implicit关键字声明的带有单个参数的函数。这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入(import)。因此通常建议将隐式转换函数的名称命名为“one2one”的形式。

常用使用方式:

  • 隐式值
  • 隐式参数
  • 隐式函数
  • 隐式增强
  • 隐式类

Scala会考虑如下位置的隐式转换函数:

  • 位于源或目标类型的伴生对象中的隐式函数
  • 位于当前作用域可以以单个标识符指代的隐式函数

隐式转换在如下三种不同情况下会被考虑:【知道】

  • 当表达式类型与预期类型不同时【隐式值的使用】
  • 当对象访问一个不存在成员时【隐式增强】
  • 当对象调用某个方法,而这个方法的参数声明与传入参数不匹配时【隐式函数】

有三种情况编译器不会尝试使用隐式转换: 【知道】

  • 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换
  • 编译器不会尝试同时执行多个转换
  • 存在二义性的转换是错误

隐式值

在声明变量或者常量添加implicit关键字

如:implicit var name:String = "zs"

package implicitconversion

/**
  * 隐式值的使用
  */
object Implicit01 {
  def main(args: Array[String]): Unit = {
    // 1. 声明
    implicit var name:String = "zs"
    // implicit var address:String = "ls"  //error
    implicit val sex:Boolean = true  //ok


    // 2. 使用
    // 注意:
    //  1. 在使用隐式值时,通过隐式值的类型获取
    //  2. 同类型的隐式值只能有一个
    val newValue =implicitly[String]
    val newValue2 =implicitly[Boolean]
    println(newValue)
    println(newValue2)
  }
}

注意:
1. 在使用隐式值时,通过隐式值的类型获取
2. 同类型的隐式值只能有一个

隐式参数

有一些特殊的方法,含有隐式参数

语法: def sayHi(implicit name:String):Unit = {}

注意: 如果方法或者函数含有隐式参数,会自动在作用域寻找隐式值赋予隐式参数

package implicitconversion

object Implicit02 {
  def main(args: Array[String]): Unit = {
    implicit val name: String = "zs"
    implicit val default: Int = 0
    sayHi // 触发隐式参数的匹配
    sayHi("ls") // 因为调用时有具体的参数值,并不会触发隐式参数的匹配

    println(sum(1)(2))  // 3
    println(sum(1))  // 1
    println(sum2)  // 0 zs
    println(sum2(10,"ls"))  // 10 ls
  }

  /**
    *
    * @param name 隐式参数:在作用域中寻找同类型隐式值  同类型隐式值赋予隐式参数
    */
  def sayHi(implicit name: String): Unit = {
    println("Hello:" + name)
  }
 
  def sum(x: Int)(implicit y: Int): Int = {
    x + y
  }

  def sum2(implicit x: Int, y: String): String = { // x和y都为隐式参数
    x + "\t" + y
  }
}

隐式函数

将一种类型自动转换为另外一种类型

package implicitconversion

object Implicit03 {
  def main(args: Array[String]): Unit = {

    sayHi(new Person("zs")) // 正常写法

    sayHi("ls")
  }

  /**
    * 隐式函数: 将一种类型转换为另外一种类型
    *
    * 注意:隐式函数不是我们调用的,是scala在编译不通过式,会在作用域寻找可以进行转换函数
    * @param
    */
  implicit def str2Person(str:String):Person={
    new Person(str)
  }

  /*
  implicit def int2String(n:Int) :String = {
    n+""
  }
  */

  def sayHi(p: Person): Unit = {
    println("Hello:" + p.name)
  }
}


class Person(var name: String) {
}

隐式增强

通过隐式转换增强某一个类的功能

package implicitconversion

/**
  * 隐式增强
  */
object Implicit04 {
  def main(args: Array[String]): Unit = {
    val man = new Man("zs")
    man.fly
  }

  // man ---> superMan
  implicit def man2SuperMan(man:Man):SuperMan = {
    new SuperMan(man.name)
  }
}

class Man(var name:String)

class SuperMan(var name:String){
  def fly={
    println("fly~~~~~~~")
  }
}

隐式类

implicit修饰的特殊类,注意不能独立声明,需要定义在一个类\对象的内部

package implicitconversion

/**
  * 隐式类
  */
object Implicit05 {
  def main(args: Array[String]): Unit = {
    val man = new Man2("zs")
    man.fly

    // 隐式类: 主构造器接受一个类型 转换为隐式类类型  man2 ---> SuperMan2
    implicit class SuperMan2(man2: Man2) {
      var name: String = ""

      def fly = {
        this.name = man2.name
        println("fly~~~~~~~~")
      }
    }
  }
}

class Man2(var name:String)

隐式转换的导入

总结:

  • 如果在当前作用域(源文件)有隐式转换,无需导入
  • 如果在当前作用域没有隐式转换,需要手动导入,使用语法import xxx
package implicitconversion

/**
  * 隐式转换的使用:
  *   - 对象中的隐式转换,使用如下的语法  对象名._
  *   - 类中隐式转换,使用如下的语法: val 引用 = new 类
  *                              import 引用._ 【语法】
  *
  * 注意:
  *   导入的隐式转换不能和当前类的成员产生命名冲突
  */
object Implicit06 {

  def main(args: Array[String]): Unit = {

    // 对象中的隐式转换,使用如下的语法
    import AA._
    val n2: Int = implicitly[Int]
    println(n2)
    val s: Boolean = implicitly[Boolean]
    println(s)

    // 类中隐式转换,使用如下的语法:
    val bb = new BB
    import bb._
    val str2: String = implicitly[String]
    println(str2)
    val c2: Char = implicitly[Char]
    println(c2)
  }
}

object AA {
  implicit val n: Int = 10
  implicit val sex: Boolean = false
}

class BB {
  implicit val str: String = "bb"

  implicit val c: Char = 'A'
}

十六、泛型

泛型规定了类或者方法的使用类型

泛型类

语法: 类名[泛型类型,....]

package generic

/**
  * 泛型类
  * T S M K
  */
class Generic01[T, S, K](var name: T, var sex: S) {

  var age: K = _

  override def toString: String = name + "\t" + sex + "\t" + age
}


object Generic01 {
  def main(args: Array[String]): Unit = {
    val g1 = new Generic01[String, Boolean, Int]("zs", false)
    g1.age = 10
    println(g1)
  }
}

泛型方法或函数

package generic

/**
  * 泛型方法或者函数
  */
object Generic02 {
  def main(args: Array[String]): Unit = {
    print[Int](Array(1, 2, 3, 4, 5))
    // print[Int](Array("a","b","c"))  //error
  }

  def print[T](arr: Array[T]): Unit = {
    arr.foreach(println)
  }
}

上边界

语法:[T <: S], 限定T类型必须是S的子类型 ,包含S类型本身

package generic.upper

class A

class B extends A

// 上边界  T类型必须A子类型
class C[T <: A](var t: T)

class D

object C{
  def main(args: Array[String]): Unit = {
    val c1 = new C[A](new A)  //A类型本身 ok
    val c2 = new C[B](new B)  //A类型的子类B ok
    // val c3 = new C[D](new D)  //A类型的子类B ERROR
  }
}

下边界

语法:[T >: S] ,限定T类型必须S的父类型,包含S类型本身

package generic.lower

class A

class B extends A

// 下边界
class C[T >: B](var t: T)

class D extends B

object C {
  def main(args: Array[String]): Unit = {
    val c1 = new C[B](new B) // 下边界本身类型 ok
    val c2 = new C[A](new A) // A类型是B类型 ok
    val c3 = new C[D](new D) // D类型是B类型子类型 不符合下边界语义 ERROR
  }
}

视图边界

语法:[T <% S], 运行时尝试将T类型隐式转换为S类型

package generic.viewbound

import java.text.SimpleDateFormat

/**
  * 视图边界【限定】
  *
  * str ---> java.util.Date
  */
class A[T <% java.util.Date](val date: T)


object A {
  def main(args: Array[String]): Unit = {
    val a = new A[String]("2019-11-20 15:21:30")
    println(a.date.getYear +"\t"+a.date.getMonth + "\t" +a.date.getDay)
  }

  // 提供一个隐式转换函数 str --> java.util.Date
  implicit def str2Date(str: String): java.util.Date = {
    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str)
  }
}

多重限定

多上界

语法:[T <: S with K] 表示T类型必须同时是S类型和K类型的子类型

package generic.multiupper

class A

class B extends A

class C extends B

// 多上界 表示:T 类型必须是B类型和A类型的子类型
// 特点:包含最小的上边界B类型、B类型和A类型的子类
class D[T <: B with A](t:T)

class E

object D{
  def main(args: Array[String]): Unit = {
    val d1 = new D[C](new C) // OK
    val d2= new D[B](new B) // OK
    // val d3= new D[A](new A) // ERROR
    // val d4= new D[E](new E) // ERROR
  }
}
多下界

语法:[T >: S with K] 表示T类型是S类型或者K类型的父类型

package generic.multilower

class A

class B extends A


class C

class D extends C

// 多下界 表示 T类型必须是B类型或者D类型的父类型
class E[T >: B with D](t: T)

// class F
class F extends D

object E{
  def main(args: Array[String]): Unit = {
    val e1 = new E[A](new A) //ok
    val e2 = new E[D](new D) //ok
    val e3 = new E[B](new B) //ok
    val e4 = new E[C](new C) //ok
    //val e5 = new E[F](new F) //error
  }
}
既有上界又有下界

语法:[T >: S <: K] 表示T类型的上边界为K类型,下边界为S类型

package generic.upper_lower

class A

class B extends A

class C extends B

// 既有上边界 又下边界
class D[T >: C <: A](var t: T)

class E extends C

object D{
  def main(args: Array[String]): Unit = {
    val d1 = new D[B](new B)  //ok
    val d2 = new D[A](new A)  //ok
    val d3 = new D[C](new C)  //ok
    // val d4 = new D[E](new E)  //error
  }
}

协变

语法:[+T]

​ 如果T类型是T1类型的父类型,则 C[T]类型是C[T1]类型的父类

class A

class B extends A

class C[+T]

C[A] ---> C[B] 父类

//---------------------------------------------------------
package generic.xb

class A

class B extends A

class C[+T]

object C {
  def main(args: Array[String]): Unit = {
    val c1 = new C[B]

    // 协变后 C[A] 依然是 C[B] 父类型
    val c2: C[A] = c1
  }
}

逆变

语法:[-T]

如果T类型是T1类型的父类型,则 C[T1]类型是C[T]类型的父类

class A

class B extends A

class C[-T]

C[B] ---> C[A] 父类
//-------------------------------------------------------
package generic.nb

class A

class B extends A

// 逆变
class C[-T]

object C{
  def main(args: Array[String]): Unit = {
    val c1 = new C[A]
    
    val c2:C[B] = c1
  }
}

不变

语法:[T] ,使用方法同泛型类和泛型方法

上下文限定

语法:[T:M] 上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。

package generic.context

// 语法:`[T:M]`
//
//    上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
class Person(var name: String, var age: Int)


/**
  * T: 泛型类(Ordering)
  *
  * @tparam T
  */
class CompareUtils[T: Ordering](p1: T, p2: T) {

  def compare: Int = {
    val ordering = implicitly[Ordering[T]] // 获取一个Ordering[T]隐式值
    ordering.compare(p1,p2)
  }
}

object Person {
  // 声明ordering[Person]的隐式值【对象】
  implicit val ordering: Ordering[Person] = new Ordering[Person] {
    override def compare(x: Person, y: Person): Int = if (x.age > y.age) 1 else -1
  }

  def main(args: Array[String]): Unit = {
    val p1 = new Person("zs", 20)
    val p2 = new Person("ls", 30)
    val tools = new CompareUtils[Person](p1, p2)
    if(tools.compare == 1){
      println("大于")
    }else{
      println("小于")
    }
  }
}

十七、集合

Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。

Scala 集合分为可变的和不可变的集合:

  • 可变集合(mutable) 可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
  • 不可变集合(默认 immutable)类永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。

集合中基本结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c8AluOGL-1575721948444)(assets/20160408203057811)]

Java中集合结构:

|- Iterable

​ |- Collection

​ |- List 有序 有小标 元素可重复

​ |- ArrayList

​ |- Vector

​ |- LinkedList

​ |- Set 无序 无下标 元素不可重复

​ |- HashSet

​ |- TreeSet

​ |- LinkedHashSet

​ |- Map K-V键值对

​ |- HashMap

​ |- HashTable

​ |- TreeMap

​ |- Properties

Seq 序列

Ranage

数值范围区间

scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8,
 9, 10)

scala> 1 until 10
res2: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> 1 until 10 by 2
res3: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

scala> new Range(1,10,2)
res4: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

scala> Range(1,10,2)
res6: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
Vector(向量集合)

树形结构实现,查找效率极高

scala> Vector(1,2,3,4,5)
res7: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5)

scala> Vector[String]("A","B","C")
res9: scala.collection.immutable.Vector[String] = Vector(A, B, C)

scala> for(n <- 1 to 10 if n % 2 == 0) yield n
res10: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
List (不可变的列表)

immutable 不可变的列表

// 声明
scala> List(1,2,3,4,5)
res11: List[Int] = List(1, 2, 3, 4, 5)

// 获取元素  下标
scala> res11(2)
res12: Int = 3

// 是否可以对不可变列表中的元素进行更改
scala> res11(2) = 13
<console>:13: error: value update is not a member of List[Int]
       res11(2) = 13
       ^

// 支持增删改操作,在新集合的基础之上完成的
// 在集合的尾部添加元素
scala> res11 :+ 6  
res15: List[Int] = List(1, 2, 3, 4, 5, 6)

scala> res11 == res15
res16: Boolean = false

scala> res11
res17: List[Int] = List(1, 2, 3, 4, 5)
    
// 将0添加到集合的头部
scala> 0 +: res11
res19: List[Int] = List(0, 1, 2, 3, 4, 5)

// 移除元素
scala> res25.drop(1)
res27: List[Int] = List(2, 3)

scala> res25 == res27
res28: Boolean = false
Nil

空列表,没有任何元素的List集合

scala> val list = Nil
list: scala.collection.immutable.Nil.type = List()
:::::

:: 如:A :: List 将A作为整体添加到List集合的头部

::: 如:A ::: List 将A中的各个元素添加到List集合的头部

scala> 1 :: Nil
res20: List[Int] = List(1)

scala> 2:: res20
res21: List[Int] = List(2, 1)

scala> 3::res21
res22: List[Int] = List(3, 2, 1)

scala> List(1,2,3) :: res22
res23: List[Any] = List(List(1, 2, 3), 3, 2, 1)

scala> List(1,2,3) ::: res22
res24: List[Int] = List(1, 2, 3, 3, 2, 1)

ListBuffer(可变列表)

mutable 可以在原始集合的基础之上,进行改操作

// 声明一
scala> scala.collection.mutable.ListBuffer(1,2,3,4,5)
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)

// 声明二
scala> import scala.collection.mutable._  // 等价于  import scala.collection.mutable.*
scala> ListBuffer("A","B")
res30: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, B)

// 使用
// 获取
scala> res1(1)
res2: String = B
// 修改
scala> res1(1) = "C"
scala> res1
res4: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, C)

// 相同的对象
scala> res4 == res1
res5: Boolean = true

// 添加
scala> res1 += "D"
res6: res1.type = ListBuffer(A, C, D)

scala> res1.+=("E")
res7: res1.type = ListBuffer(A, C, D, E)

// 移除
scala> res1 -= "A"
res9: res1.type = ListBuffer(C, D, E)

// ++= 将后一个集合中的元素追加到当前集合的末尾
scala> res1 ++= ListBuffer("X","Y","Z")
res13: res1.type = ListBuffer(D, E, C, X, Y, Z)

// A +=:ListBuffer  表示将A添加到ListBuffer头部
scala> "o" +=: res1
res14: res1.type = ListBuffer(o, D, E, C, X, Y, Z)

结论:

  • 通常情况下不可变集合的操作符是不含=,操作完成后返回一个新集合
  • 通常情况下可变集合的操作符是含有=,操作时在当前集合完成的,不会返回新集合

了解更多操作符的知识:

请关注: 资料13.7章节

遍历
scala> res1.foreach( println(_))  // “_“   代表的是集合中的每一个元素
o
D
E
C
X
Y
Z

scala> for(n <- res1){println(n)}
o
D
E
C
X
Y
Z

scala> for(n <- 0 until res1.size){println(res1(n))}
o
D
E
C
X
Y
Z

Set

Set 不可变(immutable)
// 声明
scala> scala.collection.immutable.Set[Int](1,2,3,2)
res24: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

// 获取
scala> res24.foreach(println)
1
2
3

// 改操作
scala> res24 + 4
res26: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)

scala> res24 + 3
res27: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> res24 == res26
res28: Boolean = false

scala> res24 - 2
res29: scala.collection.immutable.Set[Int] = Set(1, 3)
Set可变集合(mutable)
scala> scala.collection.mutable.Set[Int](1,2,3,3)
res31: scala.collection.mutable.Set[Int] = Set(1, 2, 3)

// 操作
scala> res31 += 4
res32: res31.type = Set(1, 2, 3, 4)

scala> res32 == res31
res33: Boolean = true

scala> res31 -= 2
res34: res31.type = Set(1, 3, 4)
// 遍历方法(略)
HastSet(略)
TreeSet(略)

Map

元素 k,v键值对

可变和不可变

// 方法一
// 可变 
scala> Map("bj"->"北京","tj"->"天津")
res35: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)

// 不可变
scala> scala.collection.immutable.Map[String,String]("bj"->"北京","tj"->"天津")
res36: scala.collection.immutable.Map[String,String] = Map(bj -> 北京, tj -> 天津)

// 方法二
scala> Map(("bj","北京"),("tj","天津"))
res37: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)

scala.collection.immutable.Map(("bj","北京"),("tj","天津"))


// 操作
// 根据k取V值
scala> res0("bj")
res1: String = 北京

// 根据k修改v(可变集合)
scala> res3("bj")="bjj"

scala> res3
res5: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> bjj)

//添加k v对
scala> res3.+=(("sh","上海"))
res6: res3.type = Map(tj -> 天津, bj -> bjj, sh -> 上海)

//根据k移除kv
scala> res3.remove("tj")
res7: Option[String] = Some(天津)

scala> res3
res8: scala.collection.mutable.Map[String,String] = Map(bj -> bjj, sh -> 上海)

// 通过声明Tuple1元组 删除kv
scala> res3 -= (("sh"))
res9: res3.type = Map(bj -> bjj)

scala> res3
res10: scala.collection.mutable.Map[String,String] = Map(bj -> bjj)

// 遍历
scala> res3.foreach(t => println(t._1 +"\t"+t._2))
bj      bjj

// 键遍历
scala> res13.foreach(k =>println(res3(k)))
bjj

// 值遍历
scala> res3.values
res15: Iterable[String] = HashMap(bjj)

Tuple(元组)【重点】

一种特殊的数据类型,可以存放任意的值,类似于ArrayList[Object]

scala中内置了22个元组Tupe1~Tuple22

通常使用Tuple2,描述Map集合的KV键值对

// 声明
scala> ("zs",1)
res18: (String, Int) = (zs,1)

scala> res18.getClass
res19: Class[_ <: (String, Int)] = class scala.Tuple2

scala> ("zs",1,false)
res20: (String, Int, Boolean) = (zs,1,false)

scala> res20.getClass
res21: Class[_ <: (String, Int, Boolean)] = class scala.Tuple3

// 获取元组中的值  语法: 元组引用名._n  获取元组中第n个值
scala> res20._1
res22: String = zs

scala> res20._2
res23: Int = 1

// 元组的遍历
scala> res20.productIterator
res24: Iterator[Any] = non-empty iterator

scala> for(n <- res24) println(n)
zs
1
false

Java和Scala集合的互操作

package collection

object JavaAndScalaCollectionOpt {
  def main(args: Array[String]): Unit = {
    // 导入隐式转换
    import scala.collection.JavaConverters._

    // 1. 创建Java集合
    val list = new java.util.ArrayList[Int]()
    list.add(1)
    list.add(2)
    list.add(3)

    // 2. java集合转化scala的集合
    val scalaList = list.asScala
    val newScalaList = scalaList :+ 4
    newScalaList.foreach(println)

    // 3. scala集合转换java集合
    val converterJavaList = newScalaList.asJava

    println(converterJavaList.get(3))
  }
}

十八、模式匹配和样例类

match pattern

模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。

但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。

值匹配

package matchpattern

/**
  * 值匹配
  */
object MatchPattern01 {
  def main(args: Array[String]): Unit = {

    val str:String = "C"

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    str match{
      case "A" => println("A")
      case "B" => println("B")
      case _ => println("其它")  // `_` 表示default 其它
    }
  }
}

if守卫

if 判断

/**
  * 值匹配
  */
object MatchPattern02 {
  def main(args: Array[String]): Unit = {

    // val age:Int = 20
    val age:Int = 45  // 类似于: if... else if... else

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    age match{
      case _ if(age > 60) => println("老年人")  // `_` 匹配任意值
      case _ if(age > 40) => println("中年人")
      case _ if(age >= 20) => println("青年人")
      case _ => println("少年&儿童")  // `_` 表示default 其它
    }
  }
}

模式匹配中的变量

package matchpattern

/**
  * 变量
  */
object MatchPattern03 {
  def main(args: Array[String]): Unit = {

    // val age:Int = 20
    val age: Int = 18 // 类似于: if... else if... else

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    age match {
      case a1 if (a1 > 60) => println("老年人:" + a1) // `_` 匹配任意值
      case a2 if (a2 > 40) => println("中年人:" + a2)
      case a3 if (a3 >= 20) => println("青年人:" + a3)
      case _default => println("少年&儿童:" + _default) // `_` 表示default 其它
    }
  }
}

类型匹配

对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用isInstanceOf操作

package matchpattern

/**
  * 类型匹配
  */
object MatchPattern04 {
  def main(args: Array[String]): Unit = {

    // val t: AnyVal = 10.0
    // val t: AnyVal = 10
    // val t: AnyVal = true
    val t: Any = "aa"

    // 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
    // if守卫
    t match {
      case d: Double => println("小数:" + d)
      case n: Int => println("整数:" + n)
      case b: Boolean => println("布尔:" + b)
      case _o => println("其它类型:" + _o)
    }
  }
}

集合元素匹配

个数,类型,内容

package matchpattern

/**
  * 集合匹配
  */
object MatchPattern05 {
  def main(args: Array[String]): Unit = {
    // val arr = Array(1,2,3,4,5)
    // val arr = Array("A","B")
    // val arr = Array('a','b','c','d')
    // val arr: Array[Any] = Array('a')

    // 匹配元素的个数【匹配数组中包含5个元素】
    /*
    arr match {
      case Array(n1,n2) => println("匹配2个元素的集合")
      case Array(n1,n2,n3,n4,n5) => println("匹配5个元素的集合")
      case Array(n1,n2,n3,_*) => println("匹配至少3个元素的集合")
      case _ => println("匹配其它个数元素的集合")
    }
    */

    // val arr:Array[Any] = Array(1,2,3,4,5)
    // val arr = Array("A","B")
    // val arr = Array('a','b','c','d')
    // val arr: Array[Any] = Array('a')

    // 匹配元素的内容
    /*
    arr match {
      case Array('a') => println("字符 ‘a’")
      case Array(1, 2, 3, 4, 5) => println("数值 1,2,3,4,5")
      case Array("a", "b") => println("字符串 a&b'")
      case _ => println("其它")
    }
    */

    // 集合元素内容类型匹配【只适用于Array类型】
    // val t = (1, "zs", true)
    // val t = (1, "zs", true,'A')
    /*
    val t = (1, "zs", true, 'A', 10.0)

    t match {
      case t3: (Int, String, Boolean) => println("tuple3[Int,String,Boolean]")
      case t4: Tuple4[Int, String, Boolean, Char] => println("tuple4[Int,String,Boolean,Char]")
      case (_n1: Int, _n2: String, _n3: Boolean, _n4: Char, _n5: Double) => println("tuple5[Int,String,Boolean,Char,Double]")
      case _ => println("other")
    }
     */

    val list = List[Int](1,2,3)

    // scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
    list match {
      case l1:List[String] => println("list[String]")
      case l2:List[Int] => println("list[Int]")
      case _ => println("other")
    }
  }
}

Case Class(样例类)

  • Scala中提供了一种特殊的类,用case class进行声明,中文也可以称作样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter方法,但是没有method。

  • case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)

  • Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()和unapply()方法,该方法接收主构造函数中相同的参数,并返回case class对象

  • case class常用于模式匹配

    package matchpattern
    
    /**
      * 样例类
      */
    object CaseClass01 {
      def main(args: Array[String]): Unit = {
        val student = new Student("zs",18)
        student.age
        student.age = 10
        student.name
        // student.name = "aa"  // ERROR
        val student2 = Student("zs",20)
    
    
      }
    }
    
    // Student 样例类  val name,只有getter没有setter
    //                var age, 有getter和setter
    case class Student(name: String, var age: Int)
    
    package matchpattern
    
    /**
      * 注意:
      *   scala的样例类 通常用于模式匹配,将对象的值拆解出来
      */
    object CaseClass02 {
      def main(args: Array[String]): Unit = {
        // val p: Person = Student2("zs",18,"101")
        // val p: Person = Teacher2("zs",18,"101")
        val p: Person = NoBody2("zs")
    
        p match {
          // unapply方法 将对象拆分为值
          case Student2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo)
          case Teacher2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo)
          case NoBody2(name) => println(name )
          case _other => println(_other)
        }
      }
    }
    
    // 抽象类
    abstract class Person
    
    
    case class Student2(name: String, age: Int, stuNo: String) extends Person
    
    case class Teacher2(name: String, age: Int, teaNo: String) extends Person
    
    case class NoBody2(name: String) extends Person
    

sealed(密封类)

密封类的所有子类都必须在与该密封类相同的文件中定义,主要作用:

  • 防止滥用继承:sealed关键字可以修饰类和特质(特质)**。密封类提供了一种约束:**不能在类定义的文件之外定义任何新的子类。如:scala的List的实现使用了sealed关键字
  • 语法校验
package matchpattern

sealed class A


// B类和A类在相同的源文件中(OK)
class B extends A

tern05 {
def main(args: Array[String]): Unit = {
// val arr = Array(1,2,3,4,5)
// val arr = Array(“A”,“B”)
// val arr = Array(‘a’,‘b’,‘c’,‘d’)
// val arr: Array[Any] = Array(‘a’)

// 匹配元素的个数【匹配数组中包含5个元素】
/*
arr match {
  case Array(n1,n2) => println("匹配2个元素的集合")
  case Array(n1,n2,n3,n4,n5) => println("匹配5个元素的集合")
  case Array(n1,n2,n3,_*) => println("匹配至少3个元素的集合")
  case _ => println("匹配其它个数元素的集合")
}
*/

// val arr:Array[Any] = Array(1,2,3,4,5)
// val arr = Array("A","B")
// val arr = Array('a','b','c','d')
// val arr: Array[Any] = Array('a')

// 匹配元素的内容
/*
arr match {
  case Array('a') => println("字符 ‘a’")
  case Array(1, 2, 3, 4, 5) => println("数值 1,2,3,4,5")
  case Array("a", "b") => println("字符串 a&b'")
  case _ => println("其它")
}
*/

// 集合元素内容类型匹配【只适用于Array类型】
// val t = (1, "zs", true)
// val t = (1, "zs", true,'A')
/*
val t = (1, "zs", true, 'A', 10.0)

t match {
  case t3: (Int, String, Boolean) => println("tuple3[Int,String,Boolean]")
  case t4: Tuple4[Int, String, Boolean, Char] => println("tuple4[Int,String,Boolean,Char]")
  case (_n1: Int, _n2: String, _n3: Boolean, _n4: Char, _n5: Double) => println("tuple5[Int,String,Boolean,Char,Double]")
  case _ => println("other")
}
 */

val list = List[Int](1,2,3)

// scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
list match {
  case l1:List[String] => println("list[String]")
  case l2:List[Int] => println("list[Int]")
  case _ => println("other")
}

}
}




### Case Class(样例类)

- Scala中提供了一种特殊的类,用case class进行声明,中文也可以称作样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter方法,但是没有method。

- case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)

- Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()和unapply()方法,该方法接收主构造函数中相同的参数,并返回case class对象

- case class常用于模式匹配

  ```scala
  package matchpattern
  
  /**
    * 样例类
    */
  object CaseClass01 {
    def main(args: Array[String]): Unit = {
      val student = new Student("zs",18)
      student.age
      student.age = 10
      student.name
      // student.name = "aa"  // ERROR
      val student2 = Student("zs",20)
  
  
    }
  }
  
  // Student 样例类  val name,只有getter没有setter
  //                var age, 有getter和setter
  case class Student(name: String, var age: Int)
package matchpattern

/**
  * 注意:
  *   scala的样例类 通常用于模式匹配,将对象的值拆解出来
  */
object CaseClass02 {
  def main(args: Array[String]): Unit = {
    // val p: Person = Student2("zs",18,"101")
    // val p: Person = Teacher2("zs",18,"101")
    val p: Person = NoBody2("zs")

    p match {
      // unapply方法 将对象拆分为值
      case Student2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo)
      case Teacher2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo)
      case NoBody2(name) => println(name )
      case _other => println(_other)
    }
  }
}

// 抽象类
abstract class Person


case class Student2(name: String, age: Int, stuNo: String) extends Person

case class Teacher2(name: String, age: Int, teaNo: String) extends Person

case class NoBody2(name: String) extends Person

sealed(密封类)

密封类的所有子类都必须在与该密封类相同的文件中定义,主要作用:

  • 防止滥用继承:sealed关键字可以修饰类和特质(特质)**。密封类提供了一种约束:**不能在类定义的文件之外定义任何新的子类。如:scala的List的实现使用了sealed关键字
  • 语法校验
package matchpattern

sealed class A


// B类和A类在相同的源文件中(OK)
class B extends A
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值