Scala重点指南

本文在Java基础上只记录比较不同的重点和难点

目录

1、Scala基本概念

1.1 Scala语言特点

1.2 代码中的简单说明

1.3 面向对象的理解

2、Scala基本知识

2.1 常量与变量

2.2 字符串的输出

2.3 键盘输入

2.4 简单的文件操作

2.5 Scala数据类型

2.5.1 Unit 类型、  Null 类型和 Nothing 类型(重点)

2.5.2 数据类型的自动转换

2.5.3 强制类型的转换

2.5.4 数值类型和 String 类型间转换

3、运算符

3.1关系运算符

3.2 Scala 运算符本质

3.3.1 范围数据循环( To )

3.3.2 范围数据循环( Until)

3.3.3 循环步长

3.3.4 循环返回值

3.3.5 循环中断

4、函数式编程

4.1 基本语法

4.2函数的定义

4.3 函数参数

4.4匿名函数

4.5 高阶函数

4.6函数柯里化&闭包

4.7控制抽象

4.8 惰性加载

5、面向对象

5.1 包对象

5.2 类和对象

5.2.1 定义类

5.2.2 属性

5.3 封装

5.4 访问权限

5.5 方法

5.6 创建对象

5.7 构造器

5.8 参数构造器

5.9 继承和多态

5.10 抽象类

5.10.1 抽象属性和抽象方法

5.11 单例对象(伴生对象)

5.11.1 单例对象语法

 5.11.2 apply 方法

5.12 特质(Trait)

5.13 类型检查和转换

6. 集合

6.1  集合简介

6.2 数组

6.2.1  不可变数组

6.2.2  可变数组

6.2.3  不可变数组与可变数组的转换

6.3  列表 List

6.3.1  不可变 List

6.3.2  可变 ListBuffer

6.4 Set 集合

6.4.1  不可变 Set

6.4.2  可变 mutable.Set

6.5 Map 集合

6.5.1  不可变 Map

6.5.2  可变 Map

6.6.3  集合计算简单函数

6.6.4  集合计算高级函数

WordCount案例

第 7 章 模式匹配

7.1  基本语法

7.2  模式守卫

7.3  模式匹配类型

7.3.1  匹配常量

7.4 变量声明中的模式匹配

7.5 for表达式中的模式匹配

第 9 章 隐式转换

9.1  隐式函数

9.2 隐式类

9.3  隐式参数



1、Scala基本概念

1.1 Scala语言特点

Scala 是一门以 Java 虚拟机( JVM )为运行环境并将 面向对象 函数式编程 的最佳特性结合在一起的
静态类型编程语言 (静态语言需要提前编译的如: Java c c++ 等,动态语言如: js )。
1 Scala 是一门 多范式 的编程语言, Scala 支持 面向对象和函数式编程 。(多范式,就是多种编程方
法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法。)
2 Scala 源代码(
.scala )会被编译成 Java 字节码(
.class ),然后运行于 JVM 之上, 并可以调用现有
Java 类库,实现两种语言的无缝对接。
3 Scala 单作为一门语言来看,非常的 简洁高效
4 Scala 在设计时,马丁 · 奥德斯基是参考了 Java 的设计思想,可以说 Scala 是源于 Java ,同时马丁 ·
德斯基也加入了自己的思想,将 函数式编程语言的特点融合到 JAVA , 因此,对于学习过 Java 的同学,
只要在学习 Scala 的过程中,搞清楚 Scala Java 相同点和不同点,就可以快速的掌握 Scala 这门语言。

1.2 代码中的简单说明

/*
    object 关键字,声明一个单例对象(伴生对象)
 */
object HelloWorld {
  /*
      main方法:从外部可以直接调用的方法
      def 方法名(参数名称:参数类型):返回值类型={方法体}
   */
  def main(args:Array[String]):Unit={
    println("hello world")
  }
}

1.3 面向对象的理解

scala中的类不能定义静态成员,而代之以定义单例对象来替代
单例对象通过object关键字来声明
单例对象中的所有方法,可以直接通过object单例对象的名字直接来调用。
一个单例对象可以绑定在一个类,当单例对象和某个类写在同一个源文件且共享一个名字,它们就产生了绑定关系。
此时单例对象称之为该类的伴生对象,类称之为该对象的伴生类。
class student(name:String,age:Int){
  def printInfo():Unit={
    println(name+" "+age+" "+student.school)
  }
}
引入伴生对象
object student{
  val school:String="666"

  def main(args: Array[String]): Unit = {
    val alice = new student("alice", 23)
    alice.printInfo()
  }
}

2、Scala基本知识

2.1 常量与变量

1 )基本语法

var  变量名 [: 变量类型] =  初始值        var i:Int = 10

val  常量名 [: 常量类型] =  初始值        val i:Int = 10

var 修饰的变量可改变,val 修饰的变量不可改

2 )

var 修饰的对象引用可以改变, val 修饰的对象则不可改变,但对象的状态(值)

却是可以改变的。(比如:自定义对象、数组、集合等等)

object TestVar {
def main(args: Array[String]): Unit = {
// p1 是 var 修饰的, p1 的属性可以变,而且 p1本身也可以变 var p1 = new Person()

p1.name = p1 = null	"dalang"
/* p2 是 val 修饰的,那么 p2本身就不可变 (即 p2 的内存地址不能变),但是 ,p2 的属性是可以变,因为属性并没有用 val 修饰。*/
val p2 = new Person()
p2.name="jinlian"
// p2 = null // 错误的,因为 p2 是 val 修饰的
}
}
class Person{
var name : String = "jinlian"

2.2 字符串的输出

1 )基本语法

 1 )字符串,通过+号连接

 2 printf 用法:字符串,通过%传值。

 3 )字符串模板(插值字符串  通过${}获取变量值

方法三的演示:要用s"" 表示在使用字符串模板
println(s"${age}的学生在学习Scala")

多行字符串,在 Scala中,利用三个双引号包围多行字符串就可以实现

val s =
"""
|select name, age
|from user
|where name="zhangsan"
""".stripMargin
println(s)

2.3 键盘输入

1 )基本语法

StdIn.readLine() StdIn.readShort() StdIn.readDouble()

2.4 简单的文件操作

 //从文件中读取数据
    Source.fromFile("C:\\Users\\Admin\\Desktop\\6.txt").foreach(print)
    //从文件中写入数据 Scala中没有,需要调用Java中的类
    val write=new PrintWriter(new File("C:\\Users\\Admin\\Desktop\\6.txt"))
    write.write("hahahahahahahahaw2jdkaosfhaljkhfa")
    write.close()

2.5 Scala数据类型

1Scala中一切数据都是对象,都是Any的子类

2Scala中数据类型分为两大类:数值类型(AnyVal)、 引用类型(AnyRef),不管是值类型还是引用类型都是

对象。

3Scala数据类型仍然遵守,低精度的值类型向高精 度值类型,自动转换(隐式转换)

4Scala中的StringOps是对Java中的String增强

5Unit:对应Java中的void ,用于方法返回值的位置,表 示方法没有返回值。Unit是一个数据类型,只有一个对象 就是() Void不是数据类型,只是一个关键字

6Null是一个类型,只有一个对象就是null 它是 所有引用类型(AnyRef)的子类

7Nothing 是所有数据类型的子类,主要用在一个函数没有明确返回值时使 用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。

Scala重点指南_第1张图片

 Scala 的整型,默认为 Int 型,声明 Long 型,须后加‘l’或‘L

var n6 = 9223372036854775807L

Scala 的浮点型常量默认为 Double 型,声明 Float 型常量,须后加‘f’或‘F’

var n7 = 2.2345678912f

2.5.1 Unit 类型、  Null 类型和 Nothing 类型(重点)

数据类型

描述

Unit

空值 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法类型。  Unit 只有一个实例值,写成() 

Null

空引用,null , Null 类型只有一个实例值 null。

Nothing

Nothing 类型在Scala 的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,比如抛出异常的时候,可以用Nothing 来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)。

2.5.2 数据类型的自动转换

 Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这

个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:Scala重点指南_第2张图片

1 )基本说明

 1 )自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成 精度大的那种数据类型 ,然后再进行计算。

 2 把精度大的数值类型赋值给精度小的数值类型时,就会报错 ,反之就会进行自动 类型转换。

 3 )(byte short)和 char 之间不会相互自动转换。

 4 byte short char 他们三者可以计算, 在计算时首先转换为 int 类型

2.5.3 强制类型的转换

自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上

强制转函数,但可能造成精度降低或溢出,格外要注意。

Java  : int num = (int)2.5

Scala : var num : Int = 2.7.toInt

2.5.4 数值类型和 String 类型间转换

1 )基本说明

在程序开发中,我们经常需要将基本数值类型转成 String 类型。或者将 String 类型转成 基本数值类型。

2 )案例实操

 1 )基本类型转 String 类型(语法 :将基本类型的值+""  即可)

 2 String 类型转基本数值类型(语法 s1.toInt s1.toFloat s1.toDouble s1.toByte、s1.toLong s1.toShort

3、运算符

3.1关系运算符

Java 

        ==比较两个变量本身的值,即两个对象在内存中的首地址 

        equals 比较字符串中所包含的内容是否相同。

Scala ==更加类似于 Java 中的 equals ,参照 jd 工具,eq类似Java==

def main(args: Array[String]): Unit = {
val s1 = "abc"
val s2 = new String("abc")
println(s1 == s2)
println(s1.eq(s2))
}
输出结果:
true
false

3.2 Scala 运算符本质

 Scala 中其实是没有运算符的 ,所有运算符都是方法。

1 )当调用对象的方法时, .可以省略

2 )如果函数参数只有一个,或者没有参数 ()可以省略

object TestOpt {
def main(args: Array[String]): Unit = {
// 标准的加法运算
val i:Int = 1.+(1)
// ( 1 )当调用对象的方法时, .可以省略
val j:Int = 1 + (1)
// ( 2 )如果函数参数只有一个,或者没有参数,  ()可以省略 val k:Int = 1 + 1
println(1.toString())
println(1 toString())
println(1 toString)
    }
}

3.3.1 范围数据循环( To 

基本语法:

for(i <- 1 to 3)

//循环嵌套
for(i <- 1 to 3; j <- 1 to 3) {
    println(" i =" + i + " j = " + j)
}

循环三次

3.3.2 范围数据循环( Until

基本语法:

for(i <-  1 until 3) {
    print(i + " ")
}
i从1到3-1=2遍历

3.3.3 循环步长

for (i <- 1 to 10 by 2) {
    println("i=" + i)
}
i表示步长

3.3.4 循环返回值

for循环的默认返回值是

val res = for(i <- 1 to 10) yield i
println(res)

yield的用法一个句话,就是在for循环中,每次循环都会产生一个值,然后将每次产生的值保存,最后组成一个集合。

说明:将遍历过程中处理的结果返回到一个新 Vector 集合中,使用 yield 关键字。

运行结果:Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

3.3.5 循环中断

1 )基本说明

Scala 内置控制结构特地去掉了 break  continue ,是为了更好的适应函数式编程 ,推 荐使用函数式的风格解决 break  continue 的功能 ,而不是一个关键字。Scala 中使用 breakable控制结构来实现 break  continue 功能。

需求 1 :采用异常的方式退出循环

def main(args: Array[String]):Unit = {
try {
    for (elem <- 1 to 10) { 
        println(elem)        
    if (elem == 5) throw new RuntimeException
        }
    }catch {
        case e =>       //和Java有所不同
     }
    println("正常结束循环")
}

需求 2 :为了简化,采用 Scala 自带的函数 ,退出循环

import scala.util.control.Breaks
def main(args: Array[String]): Unit = {
    //Breaks.breakable 用于标记
Breaks.breakable (
    for (elem <- 1 to 10) {
    println(elem)
    if (elem == 5) Breaks.break()
    }
   )
    println("正常结束循环")
}

需求 3 :对 break 进行省略

import scala.util.control.Breaks._
object TestBreak {
def main(args: Array[String]): Unit = {
breakable {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) break
        }
    }
println("正常结束循环")
    }
}

4、函数式编程

1 )面向对象编程

解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。 对象 :用户

行为 :登录、连接 JDBC、读取数据库

属性:用户名、密码

Scala 语言是一个完全面向对象编程语言。万物皆对象

对象的本质:对数据和行为的一个封装

2 )函数式编程

解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。

例如 :请求->用户名、密码->连接 JDBC->读取数据库

Scala 语言是一个完全函数式编程语言。  万物皆函数。

函数的本质:函数可以当做一个值进行传递

3 )在 Scala 中函数式编程和面向对象编程完美融合在一起了。

4.1 基本语法

def sum(x:Int,y:Int):Int={
  x+y
}

def:定义函数的关键字 sum:函数名 x:int 参数名和参数类型,最后的int为返回类型

4.2函数的定义

1 )函数定义

 1 )函数 1 :无参,无返回值

 2 )函数 2 :无参,有返回值

 3 )函数 3 :有参,无返回值

 4 )函数 4 :有参,有返回值

 5 )函数 5 :多参,无返回值

 6 )函数 6 :多参,有返回值

2 )案例实操

object TestFunctionDeclare {
def main(args: Array[String]): Unit = {
// 函数 1 :无参,无返回值
def test1 (): Unit ={
println("无参,无返回值")
}
test1()
// 函数 2 :无参,有返回值
def test2 ():String={
return "无参,有返回值"
}
println(test2())
// 函数 3 :有参,无返回值
def test3 (s:String):Unit={
println(s)
}
test3("jinlian")
// 函数 4 :有参,有返回值
def test4 (s:String):String={
return s+"有参,有返回值"
}
println(test4("hello "))
// 函数 5 :多参,无返回值
def test5 (name:String, age:Int):Unit={
println(s"$name, $age")
}
	}
}	

4.3 函数参数

object TestFunction {
def main(args: Array[String]): Unit = {
// ( 1 )可变参数
def test( s : String* ): Unit = {
println(s)
}
// 有输入参数:输出 Array
test("Hello", "Scala")
// 无输入参数:输出 List()
test()
// (2)如果参数列表中存在多个参数,那么可变参数一般放置在最后 
def test2( name : String, s: String* ): Unit = {
    println(name + "," + s)
}
test2("jinlian", "dalang")
// (3)参数默认值
def test3( name : String, age : Int = 30 ): Unit = {
println(s"$name, $age")
}
// 如果参数传递了值,那么会覆盖默认值
test3("jinlian", 20)

// 如果参数有默认值,在调用的时候,可以省略这个参数
test3("dalang")

// 一般情况下, 将有默认值的参数放置在参数列表的后面
def test4( sex : String = "男", name : String ): Unit =
    println(s"$name, $sex")
}
// Scala 函数中参数传递是,从左到右
    //test4("wusong")

//( 4 )带名参数
test4(name="ximenqing")
}
}

4.4匿名函数

1 )说明

没有名字的函数就是匿名函数。

(x:Int)=>{函数体}

x :表示输入参数类型 Int :表示输入参数类型 ;函数体: 表示具体代码逻辑

调用的方法:

 匿名函数:(name:String,age:Int) => {println(name+" "+age)}
 调用方法一:赋值法
 val fun=(name:String,age:Int) => {println(name+" "+age)}
 fun("admin",22)
方法二:把该匿名函数当成一个参数传给另一个函数
 //(String, Int) => Unit为该函数的类型,可以在下行定义尾部输入.var 即可得到下面第二行
 val fun= (name:String,age:Int) => {println(name+" "+age)}
 
val function: (String, Int) => Unit = (name:String,age:Int) => {println(name+" "+age)}
val fun=function
 def f(func: (String, Int) => Unit):Unit={
          func("abc",21)
      }
      f(fun)
等价于 f((name:String,age:Int) => {println(name+" "+age)})

匿名方法的简化:

 1 )参数的类型可以省略,会根据形参进行自动的推导

 2 )类型省略之后 ,发现只有一个参数,则圆括号可以省略 ;其他情况:没有参数和参

数超过 1 的永远不能省略圆括号。

 3 )匿名函数如果只有一行,则大括号也可以省略

 4 )如果参数只出现一次,则参数省略且后面参数可以用_代替

原始匿名方法: (name:String) => {println(name)}
( 1 )参数的类型可以省略,会根据形参进行自动的推导
val function: (String) => Unit = (name:String) => {println(name)}//本身就有类型说明String
故可以改为: 
             f((name) => {println(name)})
( 2 )类型省略之后 ,发现只有一个参数,则圆括号可以省略 ;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。
             f(name => {println(name+" "+age)})
( 3 )匿名函数如果只有一行,则大括号也可以省略
             f(name => println(name))
( 4 )如果参数只出现一次,则参数、=> 省略且后面参数可以用_代替
             f(println(_))  
( 5 )如果可以推断当前传入的println是一个函数体,则可以直接省略(_)
             f(println) 

示例:

定义一个二元运算函数:只操作两个固定的数,但是具体运算通过参数传入
    def twokey(fun : (Int,Int)=>Int):Int={
      fun(3,9)
    }
    val add = (a : Int,b : Int)=> a + b
    val minus = (a : Int,b : Int)=> a - b
    println(twokey(add))
    println(twokey(minus))

匿名函数简化
    println(twokey((a,b)=> a + b))
    println(twokey((a,b)=> a - b))
因为每个变量都只出现一次,可以用_代替,所以也可变为
    println(twokey(_+_))
    println(twokey(_-_)) 但是b-a就不行了 可以写成 -_+_

4.5 高阶函数

1 )函数可以作为值进行传递

def main(args:Array[String]):Unit={
    def foo():Int = {
      println("foo...")
      1
    }
    //()表示参数类型,此时没有参数,Int表示返回类型
    val f1:()=>Int=foo   等价于  val f1=foo _
    println(f1)    //结果为:chapter01.HelloWorld$$$Lambda$1/764977973@6debcae2 对象的引用
    println(f1())    //结果为:foo...  1     函数被调用
    对于没有参数的方法可以直接调用
    foo()
    也可以
    foo
    val s=foo()
    println(s) //结果是调用foo函数体

    //有参数
    def f(a :Int):Int={
        println("f被调用")
        a+1
  }
      val f1=f _    //空格加下划线表明它是一个函数类型
      val f2:Int=>Int=f    f1=f2//或者直接指明函数类型
      println(f1)        //chapter01.HelloWorld$$$Lambda$1/764977973@5ba23b66
      println(f1(5))     //f被调用 6   
    
  }

2 )函数可以作为参数进行传递

fff中传入op函数,op函数类型是(Int,Int)=>Int,fff中传入两个变量a,b最终得到Int类型
def fff(op:(Int,Int)=>Int,a:Int,b:Int):Int={
      op(a,b)
    }
    def add(a:Int,b:Int):Int={
      a+b
    }
     println(fff(add,6,9))
    也可以直接写成匿名函数
     println(fff((a,b)=>a-b,12,6))
     println(fff((a,b)=>a*b,6,6))
     println(fff(_+_,6,9))

3 )函数可以作为函数返回值返回

def f5():Int=>Unit={
       def f6(a:Int):Unit={
         println("f6被调用 "+a)
       }
       f6
     }

     println(f5()(5))  //结果:f6被调用 5      () 因为f6是空返回类型
     println(f5())     //结果:chapter01.HelloWorld$$$Lambda$5/1908923184@2ff4f00f 没有参数传进返回的是f6的引用

例子:将数组进行处理,将操作抽象出来,处理完毕返回新的数组

 val arr:Array[Int]=Array(12,17,34,45,56)
     def ArrayOperation(array: Array[Int],op : Int=>Int):Array[Int]={
          for (elem <- array) yield op(elem)
}
     def add(elem:Int):Int={
       elem+3
     }
    val newarr:Array[Int]=ArrayOperation(arr,add)
    val newarr2:Array[Int]=ArrayOperation(arr,_*2) === (arr,elem=>elem*2)
     println(newarr.mkString(","))
   }

    练习 2 : 定义一个函数 func ,它接收一个Int类型的参数,返回一个函数(记作f1)。
    它返回的函数f1,接收一个 String 类型的参数,同样返回一个函数(记作f2)。
    函数f2接收一个 Char 类型的参数,返回一个 Boolean 的值。
    要求调用函数 func(0) (“”) (‘0’)得到返回值为 false ,其它情况均返回true。

    def func(a:Int):String=>Char=>Boolean={     看f1的输入输出   
       def f1(s:String):Char=>Boolean={         看f2的输入输出
         def f2(c:Char):Boolean={
            if(a==0 && s=="" && c=='0')
              false
            else
              true
         }
         f2
       }
      f1
     }
     println( func(0) ("") ('0'))
     println( func(1) ("") ('0'))

匿名函数的简写
        def func(a:Int):String=>Char=>Boolean={
        (s:String)=>(c:Char)=>if(a==0 && s=="" && c=='0') false else true
     }
函数的柯里化
def func2(a:Int)(b:String)(c:Char):Boolean={
       if(a==0 && b=="" && c=='0') false else true
     }

可以看到柯里化的写法简单明了,后面还会详细说明

4.6函数柯里化&闭包

闭包 :函数式编程的标配

1 )说明

闭包 :如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

函数柯里化 :把一个参数列表的多个参数,变成多个参数列表。

def add(a:Int,b:Int):Int={
       a+b
     }
     //1.在大数据的情形下,很多数据是相同的,我们可以考虑固定一个加数,只传入一个参数
     def add1(b:Int):Int={
       4+b
     }
     def add2(b:Int):Int={
       5+b
     }
     //2.进一步扩展,考虑拓展固定加数改变的情况,比如固定加数又有很多种情况
     def add3(a:Int):Int=>Int={
       def add4(b:Int):Int={
         a+b
       }
       add4
     }
      println(add3(34)(5))
     val addby4=add3(4)
     println(addby4(7))
简化:
def add5(a:Int):Int=>Int={a+_}

柯里化的表达:

def add6(a:Int)(b:Int)={
       a+b
     }
     println(add6(4)(7))

4.7控制抽象

1 )传值调用: 把计算后的值传递过去

def f0(a:Int):Unit={
        println("a "+a)
        println("a "+a)
  }
     def f1():Int={
       println("f1被调用")
       12
     }
     f0(f1())

结果:
    f1被调用
    a 12
    a 12

2 )传名调用: 把代码传递过去,有几个参数就执行几次代码块

  • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
def f1():Int={
       println("f1被调用")
       12
     }
    
     def f0(a: =>Int)={
        println("a "+a)
        println("a "+a)
}
     f0(f1())
结果为:
    f1被调用
    a 12
    f1被调用
    a 12
用闭包实现一个函数,将 代码块 (返回值为Boolean)作为参数传入,递归调用 参数类型=>Unit,返回值类型Unit
     def mywhile(condition: =>Boolean): (=>Unit)=>Unit ={
       condition传入条件,具体操作op:执行一个代码块,且它的返回值为Unit,内层循环的返回值也为Unit
       def doloop(op: =>Unit):Unit={
         如果当前的condition为真
         if(condition){
           op
           mywhile(condition)(op)
         }
       }
       doloop  _
     }
     var n=6
      mywhile(n>=1)({
        println(n)
        n=n-1
      })
用匿名函数实现:因为函数的名称不重要,并且传入值和返回值都确定,所以都可以删掉,用=>表示它是一个lambda表达式
def mywhile(condition: =>Boolean): (=>Unit)=>Unit ={
          op=>{
          if(condition){
            op
            mywhile(condition)(op)
          }
      }

柯里化的实现:
def mywhile2(condition : =>Boolean)(op : =>Unit):Unit= {
      if (condition) {
        op
        mywhile2(condition)(op)
      }
    }
    var n=6
    mywhile2(n>=1)({
      println(n)
      n=n-1
    })

4.8 惰性加载

1 )说明

函数返回值被声明为 lazy  ,函数的执行将被推迟 ,直到我们首次对此取值,该函 数才会执行。这种函数我们称之为惰性函数。

 lazy val result: Int= sum(12, 23)
    println("1.函数调用")
    println("2.result= " + result)

    def sum(a: Int, b: Int): Int = {
      println("3.sum被调用")
      a + b
    }
结果:
    1.函数调用
    3.sum被调用    
    2.result= 35

5、面向对象

5.1 包对象

 Scala 中可以为每个包定义一个同名的包对象 ,定义在包对象中的成员 ,作为其

应包下所有 class  object 的共享变量 ,可以被直接访问。 类似全局变量,但是是在包里。

1 )定义

package object com{

val shareValue="share"

def shareMethod()={}

}

5.2 类和对象

 :可以看成一个模板

对象 :表示具体的事物

5.2.1 定义类

1 )回顾: Java 中的类

如果类是 public 的,则必须和文件名一致。

一般 ,一个.java 有一个 public 

注意 Scala 中没有public ,一个.scala 中可以写多个类。

1 )基本语法

[修饰符] class  类名 {

        类体

}

说明

 1 Scala 语法中,类并不声明为public ,所有这些类都具有公有可见性(即默认就是 public 

 2 )一个 Scala 源文件可以包含多个类

5.2.2 属性

属性是类的一个组成部分

1 )基本语法

[修饰符]  var|val  属性名称  [ :类型] =  属性值

 Bean 属性(@BeanPropetry),可以自动生成规范的 setXxx/getXxx 方法

var age: Int = _ // _表示给属性一个默认空值 必须为var类型

5.3 封装

封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它 部分只有通过被授权的操作(成员方法),才能对数据进行操作。  Java 封装操作如下,

 1 )将属性进行私有化

 2 )提供一个公共的 set 方法,用于对属性赋值

 3 )提供一个公共的 get 方法,用于获取属性的值

Scala 中的 public 属性,底层实际为 private ,并通过 get 方法(obj.field())和 set 方法 obj.field_=(value))对其进行操作。所以 Scala 并不推荐将属性设为 private ,再为其设置 public  get  set 方法的做法。但由于很多Java 框架都利用反射调用 getXXX  setXXX  法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX  setXXX 方法(通过 @BeanProperty 注解实现)。

5.4 访问权限

1 )说明

 Java 中,访问权限分为: public private protected 和默认。在 Scala 中,你可以通

过类似的修饰符达到同样的效果。但是使用上有区别。

 1 Scala  中属性和方法的默认访问权限为public ,但 Scala 中无 public 关键字。

 2 private 为私有权限,只在类的内部和伴生对象中可用。

 3 protected 为受保护权限, Scala 中受保护权限比 Java 中更严格,同类、子类可以

访问,同包无法访问。

 4 private[包名]增加包访问权限,包名下的其他类也可以使用

5.5 方法

1 )基本语法

def 方法名(参数列表) [ :返回值类型] = {

        方法体

}

class Person {
def sum(n1:Int, n2:Int) : Int = {
    n1 + n2
    }
}
object Person {
def main(args: Array[String]): Unit = {
    val person = new Person()
    println(person.sum(10, 20))
    }
}

5.6 创建对象

1 )基本语法

val | var 对象名  [ :类型]    = new  类型()

2 )案例实操

 1 val 修饰对象,不能改变对象的引用( 即:内存地址  可以改变对象属性的值。

 2 var 修饰对象, 可以修改对象的引用和修改对象的属性值

 3 )自动推导变量类型不能多态,所以多态需要显示声明

5.7 构造器

 Java 一样, Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala 类的构造器包括: 主构造器和辅助构造器

1 )基本语法

class  类名(形参列表) {    //  主构造器

        //  类体

        def   this(形参列表) {   //  辅助构造器

}

        def   this(形参列表) {   //辅助构造器可以有多个...

}

}

说明 

 1 )辅助构造器 ,函数的名称 this ,可以有多个 ,编译器通过参数的个数及类型

来区分。

 2 )辅助构造方法不能直接构建对象 必须直接或者间接调用主构造方法。

 3 )构造器调用其他另外的构造器,要求被调用构造器必须提前声明。

//( 1 )如果主构造器无参数,小括号可省略
//class Person (){
class Person {
var name: String = _
var age: Int = _
def this(age: Int) {
    this()
    this.age = age
    println("辅助构造器")
}
def this(age: Int, name: String) {
    this(age)
    this.name = name
}
    println("主构造器")
}
object Person {
def main(args: Array[String]): Unit = {
    val person2 = new Person(18)
}
}

5.8 参数构造器

1 )说明

Scala 类的主构造器函数的形参包括三种类型:未用任何修饰、  var 修饰、  val 修饰

 1 )未用任何修饰符修饰,这个参数就是一个局部变量

 2 var 修饰参数,作为类的成员属性使用,可以修改

 3 val 修饰参数,作为类只读属性使用,不能修改

class Person(name: String, var age: Int, val sex: String) {
}
object Test {
  def main(args: Array[String]): Unit = {
    var person = new Person("bobo", 18, "男")
    //person.name 不能调用  可以在类中写一个打印方法,以此来调用
    person.age = 19
    println(person.age)
    }
  }

所以建议构造器直接明确修饰符

5.9 继承和多态

1 )基本语法

class  子类名 extends  父类名   {  类体 }

 1 )子类继承父类的属性方法

 2 scala 是单继承

父类的引用指向子类的对象 Java编译看左边,执行看右边,父类没有的方法不能调用,调用的是子类重写的方法。

Person p1 = new Man;

2 )案例实操

 1 )子类继承父类的属性方法

 2 )继承的调用顺序:父类构造器->子类构造器

3 )动态绑定

Scala 中属性和方法都是动态绑定,而 Java 中只有方法为动态绑定,静态绑定属性。

Scala重点指南_第3张图片

    Scala重点指南_第4张图片 

Scala重点指南_第5张图片

5.10 抽象类

5.10.1 抽象属性和抽象方法

1 )基本语法

 1 )定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类

 2 )定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性

 3 )定义抽象方法:def   hello():String //只声明而没有实现的方法,就是抽象方法

2 )继承&重写

 1 )如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明 为抽象类

 2 )重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override

 3 )子类中调用父类的方法使用 super 关键字

 4 )子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;

子类对非抽象属性重写 ,父类非抽象属性只支持 val 类型,而不支持 var

因为 var 修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写

5.11 单例对象(伴生对象)

Scala语言是完全面向对象的语言 ,所以并没有静态的操作( 即在Scala中没有静态的概 念) 。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象 模拟类对象 ,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的生对象 ,这个类的所有 “静态”内容都可以放置在它的伴生对象中声明。

5.11.1 单例对象语法

1 )基本语法

object Person{

        val country:String="China"

}

2 )说明

 1 )单例对象采用 object 关键字声明

 2 )单例对象对应的类称之为伴生类 ,伴生对象的名称应该和伴生类名一致。

 3 )单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

Scala重点指南_第6张图片

//( 1 )伴生对象采用 object 关键字声明
object Person {
    var country: String = "China"
    
    def newPerson(name:String):Person=new Person(name)
}
//( 2 )伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class  private Person {
    var name: String = "bobo"
}
object Test {
def main(args: Array[String]): Unit = {
//( 3 )伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访 问。
println(Person.country)

    如果不想在外面直接new创建一个对象的话,可以在伴生对象中写一个new方法,外界直接调用这个方法,把该类变为private
    val p1=Person.newPerson("xiaohe")
}
}

 5.11.2 apply 方法

1 )说明

 1 )通过伴生对象 apply 方法, 实现不使用 new 方法创建对象。

 2 )如果想让主构造器变成私有的,可以在()之前加上private

 3 apply 方法可以重载。

 4 Scala  obj(arg)的语句实际是在调用该对象的 apply 方法,即 obj.apply(arg)。用

以统一面向对象编程和函数式编程的风格。

 5 )当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构 建对象时,调用的其实时伴生对象的 apply 方法。

object Test {
def main(args: Array[String]): Unit = {
//( 1 )通过伴生对象的 apply 方法,实现不使用 new 关键字创建对象。 val p1 = Person()
println("p1.name=" + p1.name)
val p2 = Person("bobo")
println("p2.name=" + p2.name)
}
}
//( 2 )如果想让主构造器变成私有的,可以在()之前加上 private class Person private(cName: String) {
var name: String = cName
}
object Person {
def apply(): Person = {
println("apply 空参被调用")
new Person("xx")
}
def apply(name: String): Person = {
println("apply 有参被调用")
new Person(name)
}
//注意:也可以创建其它类型对象,并不一定是伴生类对象
}

5.12 特质Trait

Scala 语言中,采用特质 trait (特征)来代替接口的概念 ,也就是说,多个类具有相同

的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明。

Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法 一个类可

以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。

Scala 引入 trait 特征,第一可以替代 Java 的接口,第二个也是对单继承机制的一种

补充。

1 )基本语法

trait 特质名  {

        trait 主体

}

2 )案例实操

trait PersonTrait {

// 声明属性

        var name:String = _

// 声明方法

        def eat():Unit={

}

// 抽象属性

        var age:Int

// 抽象方法

        def say():Unit

}

特质基本语法

一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素, 所以在使用时,也采用了 extends 关键字 ,如果有多个特质或存在父类,那么需要采用 with

关键字连接。

1 )基本语法:

没有父类 class    类名 extends   特质 1     with       特质 2     with      特质 3 

有父类 class    类名   extends    父类     with   特质 1     with      特质 2   with 特质 3…

2 )说明

 1 )类和特质的关系:使用继承的关系。

 2 )当一个类去继承特质时,第一个连接词是 extends ,后面是 with

 3 )如果一个类在同时继承特质和父类时,应当把父类写在 extends 后。

3 )案例实操

 1 )特质可以同时拥有抽象方法和具体方法

 2 )一个类可以混入(mixin)多个特质

 3 )所有的 Java 接口都可以当做 Scala 特质使用

 4 动态混入 :可灵活的扩展类的功能

 4.1 )动态混入:创建对象时混入 trait ,而无需使类混入该 trait

比如唱歌,有些人不会唱歌,有些人会唱歌,那么在创建对象的时候,会唱歌的人就去使用唱歌这个trait

 4.2 )如果混入的 trait 中有未实现的方法,则需要实现

trait PersonTrait {
//( 1 )特质可以同时拥有抽象方法和具体方法
// 声明属性
var name: String = _
// 抽象属性
var age: Int
// 声明方法
def eat(): Unit = {
println("eat")
}
// 抽象方法
def say(): Unit
}
trait SexTrait {
var sex: String
}
//( 2 )一个类可以实现/继承多个特质
//( 3 )所有的 Java 接口都可以当做 Scala 特质使用
class Teacher extends PersonTrait with java.io.Serializable {
override def say(): Unit = {
println("say")
}
override var age: Int = _
}

object TestTrait {
def main(args: Array[String]): Unit = {
val teacher = new Teacher
teacher.say()
teacher.eat ()
//( 4 )动态混入:可灵活的扩展类的功能
val t2 = new Teacher with SexTrait {
override var sex: String = "男"
}
//调用混入 trait 的属性
println(t2.sex)
}
}

由于一个类可以混入(mixin)多个 trait ,且 trait 中可以有具体的属性和方法,若混入 的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。 冲突分为以下两种:

第一种,一个类( Sub )混入的两个 trait TraitA TraitB )中具有相同的具体方法,且

两个 trait 之间没有任何关系,解决这类冲突问题,直接在类( Sub )中重写冲突方法。

super调用原则:从后往前

5.13 类型检查和转换

1 )说明

 1 obj.isInstanceOf[T] :判断 obj 是不是 T 类型。

 2 obj.asInstanceOf[T] :将 obj 强转成 T 类型。

 3 classOf获取对象的类名。

class Person{
}
object Person {
def main(args: Array[String]): Unit = {
val person = new Person
//( 1 )判断对象是否为某个类型的实例
val bool: Boolean = person.isInstanceOf[Person]
if ( bool ) {
//( 2 )将对象转换为某个类型的实例
val p1: Person = person.asInstanceOf[Person] println(p1)
}
//( 3 )获取类的信息
val pClass: Class[Person] = classOf[Person]
println(pClass)
}
}

6. 集合

6.1  集合简介

1 Scala 的集合有三大类:序列 Seq、集 Set 映射 Map ,所有的集合都扩展自 Iterable 特质。

2 )对于几乎所有的集合类,Scala 都同时提供了可变不可变的版本,分别位于以下两 个包

不可变集合: scala.collection.immutable

可变集合:     scala.collection.mutable

3 Scala 不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而 不会对原对象进行修改。类似于 java 中的 String 对象

4 )可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似  java  StringBuilder 对象

6.2 数组

6.2.1  不可变数组

1 )第一种方式定义数组

定义 val arr1 = new Array[Int](10)

 1 new 是关键字

 2 [Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定 Any

 3 (10) ,表示数组的大小,确定后就不可以变化

( 3.1 )查看数组
println(arr01.mkString(","))
( 3.2 )普通遍历
for (i <- arr01) {
println(i)
}
( 3.3 )简化遍历
def printx(elem:Int): Unit = {
println(elem)
}
foreach里要传一个函数
arr01.foreach(printx)
// arr01.foreach((x)=>{println(x)})
// arr01.foreach(println(_))
arr01.foreach(println)

(3.4)循环遍历
for(i<- 0 until arr.length)等价于 i<-arr.indices
println(arr(i))

3 )第二种方式定义数组

val arr1 = Array(1, 2)

 1 )在定义数组时,直接赋初始值

 2 )使用 apply 方法伴生对象创建数组对象使用的时候可以省略 .apply

6.2.2  可变数组

1 )定义变长数组

val arr01 = ArrayBuffer[Any](3, 2, 5)

 1 [Any]存放任意数据类型

 2 (3, 2, 5)初始化好的三个元素

 3 ArrayBuffer 需要引入 scala.collection.mutable.ArrayBuffer

6.2.3  不可变数组与可变数组的转换

1 )说明

arr1.toBuffer   //不可变数组转可变数组

arr2.toArray   //可变数组转不可变数组

 1 arr2.toArray 返回结果才是一个不可变数组, arr2 本身没有变化

2 arr1.toBuffer 返回结果才是一个可变数组, arr1 本身没有变化

6.3  列表 List

6.3.1  不可变 List

1 )说明

 1 List 默认为不可变集合

 2 )创建一个 List(数据有顺序,可重复)

 3 )遍历 List

 4 List 增加数据

 5 )集合间合并:将一个整体拆成一个一个的个体,称为扁平化

 6 )取指定数据

 7 )空集合 Nil

( 1 )List 默认为不可变集合
( 2 )创建一个 List(数据有顺序,可重复)
val list: List[Int] = List(1,2,3,4,3)
=====val list=List(1,3,5,6)
( 7 )空集合 Nil
val list5 = 1::2::3::4::Nil
( 4 )List 增加数据
( 4.1 )::的运算规则从右向左
val list1 = 5::list
val list1 = 7::6::5::list
( 4.2 )添加到第一个元素位置
val list2 = list.+:(5)
	   val list2=list.:+(10)//在列表后面+

( 5 )集合间合并:将一个整体拆成一个一个的个体,称为扁平化 val list3 = List(8,9)
//val list4 = list3::list1
val list4 = list3:::list1 合并3和1
( 6 )取指定数据
println(list(0))
( 3 )遍历 List
//list.foreach(println)
//list1.foreach(println)
//list3.foreach(println)
//list4.foreach(println)
list5.foreach(println)

6.3.2  可变 ListBuffer

1 )说明

 1 )创建一个可变集合 ListBuffer

 2 )向集合中添加数据

 3 )打印集合数据

( 1 )创建一个可变集合
val buffer = ListBuffer (1,2,3,4)
( 2 )向集合中添加数据
buffer.+=(5)
buffer.append(6)
buffer.insert(1,2)
( 3 )打印集合数据
buffer.foreach (println)
( 4 )修改数据
buffer(1) = 6
buffer.update(1,7)
( 5 )删除数据
buffer.- (5)
buffer.-=(5)
buffer.remove(5)

6.4 Set 集合

默认情况下,  Scala  使用的是不可变集合, 如果你想使用可变集合, 需要引用

scala.collection.mutable.Set 

6.4.1  不可变 Set

1 )说明

 1 Set 默认是不可变集合,数据无序

 2 )数据不可重复

 3 )遍历集合

// 1 Set 默认是不可变集合,数据无序

val set = Set(1,2,3,4,5,6)

// 2 )数据不可重复

val set1 = Set(1,2,3,4,5,6,3)

// 3 )遍历集合

for(x<-set1){println(x)}

6.4.2  可变 mutable.Set

1 )说明

 1 )创建可变集合 mutable.Set

6.5 Map 集合

Scala 中的 Map  Java 类似,也是一个散列表 ,它存储的内容也是键值对(key-value

映射

6.5.1  不可变 Map

1 )说明

 1 )创建不可变集合 Map

 2 )循环打印

 3 )访问数据

 4 )如果 key 不存在,返回 0

// 1 )创建不可变集合 Map

val map = Map( "a"->1, "b"->2, "c"->3 )

// 3 )访问数据

for (elem <- map.keys) {

// 使用 get 访问 map 集合的数据,会返回特殊类型 Option(选项): 有值( Some ),无值 (None)再次get即可获得数值

println(elem + "=" + map.get(elem).get)

}

// 4 )如果 key 不存在,返回 0

println(map.get("d").getOrElse(0))

println(map.getOrElse("d", 0))

// 2 )循环打印

map.foreach((kv)=>{println(kv)})

map.foreach(println)

6.5.2  可变 Map

1 )说明

 1 )创建可变集合

 2 )打印集合

 3 )向集合增加数据

 4 )删除数据

 5 )修改数据

// 1 )创建可变集合

val map = mutable.Map( "a"->1, "b"->2, "c"->3 )

// 3 )向集合增加数据

map.+=("d"->4)

// 将数值 4 添加到集合,并把集合中原值 1 返回

val maybeInt: Option[Int] = map.put("a", 4)

println(maybeInt.getOrElse(0))

// 4 )删除数据

map.-=("b", "c")

// 5 )修改数据

map.update("d",5)

map("d") = 5

// 2 )打印集合

map.foreach((kv)=>{println(kv)})

6.6.3  集合计算简单函数

1 )说明

 1 )求和 .sum

 2 )求乘积 .product

 3 )最大值 .max

 4 )最小值 .min

 5 )排序 

//  5.1 )按照元素大小排序

println(list.sortBy(x => x))

//  5.2 )按照元素的绝对值大小排序

println(list.sortBy(x => x.abs))

//  5.3 )按元素大小升序排序

println(list.sortWith((x, y) => x < y))

//  5.4 )按元素大小降序排序

println(list.sortWith((x, y) => x > y))

6.6.4  集合计算高级函数

1 )说明

 1 )过滤

遍历一个集合并从中获取满足指定条件的元素组成一个新的集合

2 转化/映射(map

将集合中的每一个元素映射到某一个函数

 3 )扁平化

 4 )扁平化+映射 注: flatMap 相当于先进行 map 操作,在进行 flatten 操作

集合中的每个元素的子元素映射到某个函数并返回新集合

 5 分组(group)

按照指定的规则对集合的元素进行分组

 6 )简化(归约 

 7 )折叠

package chapter02

import scala.collection.mutable


class Person(name: String, var age: Int, val sex: String) {
}
object Test {
  def main(args: Array[String]): Unit = {
    val map1 = Map("a"->1, "b"->2, "c"->3)
    val map2 = mutable.Map("a"->4, "b"->5, "d"->6)
    //覆盖:println(map1++map2)
    //以map2作为基准,map2必须为mutable
    val map3=map1.foldLeft(map2)(
      (mergedMap,kv)=>{
          val key=kv._1
          val value=kv._2
        //判断当前的k是否在map1中,有就直接加没有就以0做计算
        mergedMap(key)=mergedMap.getOrElse(key,0)+value
        mergedMap
      }
    )
    println(map3) //Map(b -> 7, d -> 6, a -> 5, c -> 3)
  }
}

WordCount案例

package chapter02

object WordCount {
  def main(args: Array[String]): Unit = {
    val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello hsk")
    //1.对字符串按照空格进行切分
    val list1: List[Array[String]] =stringList.map(_.split(" "))
    //2.扁平化处理
    val list2: List[String] =list1.flatten
    println(list2)
    //1.+2.等价于
    val list3=stringList.flatMap(_.split(" "))

    //分组操作
    val groupMap: Map[String, List[String]] = list3.groupBy(word => word)
    println(groupMap)

    //对分组后的List取长度,得到出现的单词次数
    val map = groupMap.map(kv => (kv._1, kv._2.length))
    println(map)

    //将map转化为list,并排序取前三
    val  sortlist : List[(String,Int)] = map.toList.sortWith(_._2 > _._2)//根据第二个元素进行排序
    println(sortlist)
    println(sortlist.take(3))//输出前三名

    println("===============================================================================")
    val tuples = List(("Hello Scala Spark World", 4), ("Hello Scala Spark hsk", 3), ("Hello Scala", 2), ("Hello", 1))
    //方法一:先展开
//    val newtuplelist:List[String]=tuples.map(
//      kv => (kv._1.trim+" ")* kv._2 //避免直接乘导致英文单词中间没有空格
//    )
//    val endlist =newtuplelist.flatMap(_.split(" ")).groupBy(word =>word).map(kv => (kv._1,kv._2.length))
//      .toList.sortBy(_._2)
//    println(endlist)
    println("==============================================================================")
    //方法二:基于预先统计结果进行转化
    val preCountList : List[(String,Int)] =tuples.flatMap(
      tuples =>{
        var strings : Array[String] = tuples._1.split(" ") //对里面的元组进行切割
        strings.map(word =>(word,tuples._2))
      }
    )
    println(preCountList)//List((Hello,4), (Scala,4), (Spark,4), (World,4), (Hello,3), (Scala,3), (Spark,3), (hsk,3), (Hello,2), (Scala,2), (Hello,1))
    val preCountMap : Map[String,List[(String,Int)]] = preCountList.groupBy(tuples => tuples._1)//把map中的单词根据tuples._1分组
    println(preCountMap)//Map(Scala -> List((Scala,4), (Scala,3), (Scala,2)), Hello -> List((Hello,4), (Hello,3), (Hello,2), (Hello,1)), hsk -> List((hsk,3)), Spark -> List((Spark,4), (Spark,3)), World -> List((World,4)))
    val countmap: Map[String,Int] = preCountMap.mapValues(  //k不变只改变values
      tuplelist =>tuplelist.map(_._2).sum //_.map(_._2).sum
    )
    println(countmap)//Map(Scala -> 9, Hello -> 10, hsk -> 3, Spark -> 7, World -> 4)
    val countlist=countmap.toList.sortWith(_._2 > _._2)
    println(countlist)//List((Hello,10), (Scala,9), (Spark,7), (World,4), (hsk,3))
  }
}

 7  模式匹配

Scala 中的模式匹配类似于 Java 中的 switch 语法

但是 scala 从语法中补充了更多的功能,所以更加强大。

7.1  基本语法

模式匹配语法中,采用 match 关键字声明,每个分支采用 case 声关键字进行明,当需

要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹

配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _分支,

类似于 Java default 语句。

var a: Int = 10

var b: Int = 20

var operator: Char = 'd'

var result = operator match {

case '+' => a + b

case '-' => a - b

case '*' => a * b

case '/' => a / b

case _ => "illegal"

}

println(result)

1 )说明

 1 )如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java  default 语句,

若此时没有 case _ 分支,那么会抛出 MatchError

 2 )每个 case 中,不需要使用 break 语句,自动中断 case

 3 match case 语句可以匹配任何类型,而不只是字面量。

 4 =>  后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行 ,可以

使用{}括起来,也可以不括。

7.2  模式守卫

1 )说明

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

def abs(x: Int) = x match {

        case i: Int if i >= 0 => i

        case j: Int if j < 0 => -j

        case _ => "type illegal"

}

println(abs(-5))

7.3  模式匹配类型

7.3.1  匹配常量

1 )说明

Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。

def main(args: Array[String]): Unit = {
println(describe(6))
}
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
需要进行类型判断时,可以使用前文所学的 isInstanceOf[T]和 asInstanceOf[T] ,也可使
用模式匹配实现同样的功能。
def describe(x: Any) = x match {
case i: Int => "Int"
case s: String => "String hello"
case m: List[_] => "List"
case c: Array[Int] => "Array[Int]"
case someThing => "something else " + someThing }
def main(args: Array[String]): Unit = {
//泛型擦除
println(describe(List(1, 2, 3, 4, 5)))
//数组例外,可保留泛型
println(describe(Array(1, 2, 3, 4, 5, 6)))
println(describe(Array("abc")))
}

匹配数组

for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对 一个数组集合进行遍历
val result = arr match {
case Array(0) => "0" //匹配 Array(0) 这个数组
case Array(x, y) => x + "," + y //匹配有两个元素的数 组,然后将将元素值赋给对应的 x,y
case Array(0, _*) => "以 0 开头的数组" //匹配以 0 开头和
数组
case _ => "something else"
}
println("result = " + result)
}

7.4 变量声明中的模式匹配

package com.atguigu.chapter8

case class Person(name: String, age: Int)

object TestMatchVariable {
def main(args: Array[String]): Unit = {
val (x, y) = (1, 2)
println(s"x=x,y=y")

val Array(first, second, _*) = Array(1, 7, 2, 9) 
println(s"first=first,second=second")


val Person(name, age) = Person("zhangsan", 16)
println(s"name=name,age=age")
}

}

7.5 for表达式中的模式匹配

package com.atguigu.chapter8

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

val map = Map("A" -> 1, "B" -> 0, "C" -> 3)

for ((k, v) <- map) { // 直接将map中的k-v遍历出来
println(k + "->" + v)
}
println("---------")

// 遍历value = 0 的k-v,如果v不是0,过滤
for ((k, 0) <- map) {
println(k + "-->" + 0)
}

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

for ((k, v) <- map if v >= 1) {
println(k + "-->" + v)
}

}
}
 

 9  隐式转换

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用

于将类型进行转换,实现二次编译

9.1  隐式函数

1 )说明

隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

2 )案例实操

需求:通过隐式转化为 Int 类型增加方法。

class MyRichInt(val self: Int) {

def myMax(i: Int): Int = {

        if (self < i) i else self

}

def myMin(i: Int): Int = {

        if (self < i) self else i

}

}

object TestImplicitFunction {

// 使用 implicit 关键字声明的函数称之为隐式函数

implicit def convert(arg: Int): MyRichInt = {

        new MyRichInt(arg)

}

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

// 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范 围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换

println(2.myMax(6))  

//正常情况下我们不能直接2.myMax(),需要new一个对象才能使用,这时候隐式函数就出来了

}

}

9.2 隐式类

 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可

以扩展类的功能,在集合中隐式类会发挥重要的作用。

1 )隐式类说明

 1 )其所带的构造参数有且只能有一个

 2 )隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是级的

object TestImplicitClass {

implicit class MyRichInt(arg: Int) {

def myMax(i: if (arg <

}

def myMin(i: if (arg <

}

}

Int): Int = { i) i else arg

Int) = {      i) arg else i

def main(args: Array[String]): Unit = { println(1.myMax(3))

}

}

9.3  隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时, 就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

1 )说明

 1 )同一个作用域中,相同类型的隐式值只能有一个

 2 )编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。

 3 )隐式参数优先于默认参数

object TestImplicitParameter {

implicit val str: String = "hello world!"

def hello(implicit arg: String="good bey world!"): Unit = { println(arg)

}

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

        hello   //隐式参数在底层是柯里化的表达,不用括号

}

}

Scala重点指南_第7张图片

 

你可能感兴趣的:(大数据,scala,开发语言,后端)