Scala基础-2

面向对象

Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。

Scala 中语法和 Java 不同,补充了更多的功能。

Scala 包

  1. 基本语法

    package 包名

  2. Scala 包的三大作用(和 Java 一样)

    1. 区分相同名字的类

    2. 当类很多时,可以很好的管理类

    3. 控制访问范围

包的命名

命名规则

只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。

案例实操

1
2
demo.class.exec1 //错误,因为 class 关键字
demo.12a //错误,数字开头

命名规范

1
2
一般是小写字母+小圆点
com.公司名.项目名.业务模块名

案例实操

1
2
3
com.atguigu.oa.model 
com.atguigu.oa.controller
com.sohu.bank.order

包说明(包语句)

  1. 说明

    Scala 有两种包的管理风格,一种方式和 Java 的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如com.atguigu.scala。另一种风格,通过嵌套的风格表示层级关系,如下

    1
    2
    3
    4
    5
    6
    package com {
    package atguigu {
    package scala {
    }
    }
    }
    1. 一个源文件中可以声明多个 package
    2. 子包中的类可以直接访问父包中的内容,而无需导包
  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com {

    import com.atguigu.Inner //父包访问子包需要导包
    object Outer {
    val out: String = "out"

    def main(args: Array[String]): Unit = {
    println(Inner.in)
    }
    }
    package atguigu {

    object Inner {
    val in: String = "in"

    def main(args: Array[String]): Unit = {
    println(Outer.out) //子包访问父包无需导包
    }
    }

    }

    }

    package other {
    }

包对象

在 Scala 中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有 class 和 object 的共享变量,可以被直接访问。

  1. 定义

    1
    2
    3
    4
    package object com{
    val shareValue="share"
    def shareMethod()={}
    }
  2. 说明

    1. 若使用 Java 的包管理风格,则包对象一般定义在其对应包下的 package.scala文件中,包对象名与包名保持一致。

    2. 如采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      package com {

      object Outer {
      val out: String = "out"

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

      }

      package object com {
      val name: String = "com"
      }

导包说明

  1. 和 Java 一样,可以在顶部使用 import 导入,在这个文件中的所有类都可以使用。

  2. 局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用

  3. 通配符导入:import java.util._

  4. 给类起名:import java.util.{ArrayList=>JL}

  5. 导入相同包的多个类:import java.util.{HashSet, ArrayList}

  6. 屏蔽类:import java.util.{ArrayList =>,}

  7. 导入包的绝对路径:new root.java.util.HashMap

    1
    2
    3
    4
    5
    6
    7
    8
    9
    package java {
    package util {

    class HashMap {
    }

    }

    }

说明

import com.atguigu.Fruit 引入 com.atguigu 包下Fruit(class 和 object)
import com.atguigu._ 引入 com.atguigu 下的所有成员
import com.atguigu.Fruit._ 引入 Fruit(object)的所有成员
import com.atguigu.{Fruit,Vegetable} 引入 com.atguigu 下的Fruit 和 Vegetable
import com.atguigu.{Fruit=>Shuiguo} 引入 com.atguigu 包下的 Fruit 并更名为 Shuiguo
import com.atguigu.{Fruit=>Shuiguo,_} 引入 com.atguigu 包下的所有成员,并将 Fruit 更名为 Shuiguo
import com.atguigu.{Fruit=>,} 引入 com.atguigu 包下屏蔽 Fruit 类
new root.java.util.HashMap 引入的 Java 的绝对路径

注意:

Scala 中的三个默认导入分别是

1
2
3
import java.lang._
import scala._
import scala.Predef._

类和对象

类:可以看成一个模板

对象:表示具体的事物

定义类

  1. 回顾:Java 中的类

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

    一般,一个.java 有一个 public 类

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

  2. 基本语法

    1
    2
    3
    [修饰符] class 类名 {
    类体
    }

    说明

    1. Scala 语法中,类并不声明为 public,所有这些类都具有公有可见性(即默认就是
      public)
    2. 一个 Scala 源文件可以包含多个类
  3. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    package com.atguigu.chapter06
    //(1)Scala 语法中,类并不声明为 public,所有这些类都具有公有可见性(即默认就是 public)
    class Person {
    }

    //(2)一个 Scala 源文件可以包含多个类
    class Teacher {
    }

属性

属性是类的一个组成部分

  1. 基本语法

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

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

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.atguigu.scala.test

    import scala.beans.BeanProperty

    class Person {
    var name: String = "bobo" //定义属性
    var age: Int = _ // _表示给属性一个默认值
    //Bean 属性(@BeanProperty)
    @BeanProperty var sex: String = "男"
    //val 修饰的属性不能赋默认值,必须显示指定
    }

    object Person {
    def main(args: Array[String]): Unit = {
    var person = new Person()
    println(person.name)
    person.setSex("女")
    println(person.getSex)
    }
    }

封装

封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。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 注解实现)。

访问权限

  1. 说明

    在 Java 中,访问权限分为:public,private,protected 和默认。在 Scala 中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。

    1. Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。
    2. private 为私有权限,只在类的内部和伴生对象中可用。
    3. protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以访问,同包无法访问。
    4. private[包名]增加包访问权限,包名下的其他类也可以使用
  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package com.atguigu.scala.test
    class Person {
    private var name: String = "bobo"
    protected var age: Int = 18
    private[test] var sex: String = "男"

    def say(): Unit = {
    println(name)
    }
    }

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

    class Teacher extends Person {
    def test(): Unit = {
    this.age
    this.sex
    }
    }

    class Animal {
    def test: Unit = {
    new Person().sex
    }
    }

方法

  1. 基本语法

    1
    2
    3
    def 方法名(参数列表) [:返回值类型] = { 
    方法体
    }
  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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))
    }
    }

创建对象

  1. 基本语法

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

  2. 案例实操

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

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

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      class Person {
      var name: String = "canglaoshi"
      }

      object Person {
      def main(args: Array[String]): Unit = {
      //val 修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
      val person = new Person()
      person.name = "bobo"
      // person = new Person()// 错误的
      println(person.name)
      }
      }

构造器

和 Java 一样,Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。Scala 类的构造器包括:主构造器和辅助构造器

  1. 基本语法

    1
    2
    3
    4
    5
    6
    class 类名(形参列表) { // 主构造器
    // 类体
    def this(形参列表) { // 辅助构造器
    }
    def this(形参列表) { //辅助构造器可以有多个...
    } }

    说明:

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

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

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

  2. 案例实操

    1. 如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      //(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)
      }
      }

构造器参数

  1. 说明

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

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

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

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

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Person(name: String, var age: Int, val sex: String) {
    }

    object Test {
    def main(args: Array[String]): Unit = {
    var person = new Person("bobo", 18, "男")
    // (1)未用任何修饰符修饰,这个参数就是一个局部变量
    // printf(person.name)
    // (2)var 修饰参数,作为类的成员属性使用,可以修改
    person.age = 19
    println(person.age)
    // (3)val 修饰参数,作为类的只读属性使用,不能修改
    // person.sex = "女"
    println(person.sex)
    }
    }

继承和多态

基本语法

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

  1. 子类继承父类的属性和方法
  2. scala 是单继承

案例实操

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Person(nameParam: String) {
var name = nameParam
var age: Int = _

def this(nameParam: String, ageParam: Int) {
this(nameParam)
this.age = ageParam
println("父类辅助构造器")
}

println("父类主构造器")
}

class Emp(nameParam: String, ageParam: Int) extends Person(nameParam, ageParam) {
var empNo: Int = _

def this(nameParam: String, ageParam: Int, empNoParam: Int) {
this(nameParam, ageParam)
this.empNo = empNoParam
println("子类的辅助构造器")
}

println("子类主构造器")
}

object Test {
def main(args: Array[String]): Unit = {
new Emp("z3", 11, 1001)
}
}

动态绑定:

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

案例实操(对比 Java 与 Scala 的重写):

Scala

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Person {
val name: String = "person"

def hello(): Unit = {
println("hello person")
}
}

class Teacher extends Person {
override val name: String = "teacher"

override def hello(): Unit = {
println("hello teacher")
}
}

object Test {
def main(args: Array[String]): Unit = {
val teacher: Teacher = new Teacher()
println(teacher.name)
teacher.hello()
val teacher1: Person = new Teacher
println(teacher1.name)
teacher1.hello()
}
}

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Person {
public String name = "person";

public void hello() {
System.out.println("hello person");
}
}

class Teacher extends Person {
public String name = "teacher";

@Override
public void hello() {
System.out.println("hello teacher");
}
}

public class TestDynamic {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Person teacher1 = new Teacher();
System.out.println(teacher.name);
teacher.hello();
System.out.println(teacher1.name);
teacher1.hello();
}
}

结果对比

抽象类

抽象属性和抽象方法

  1. 基本语法

    1. 定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
    2. 定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
    3. 定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法

    案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    abstract class Person {
    val name:String

    def hello():Unit
    }

    class Teacher extends Person {
    val name:String ="teacher"

    def hello():Unit =

    {
    println("hello teacher")
    }
    }
  2. 继承&重写

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

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

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

    4. 子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var。因为 var 修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写

匿名子类

说明

和 Java 一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class Person {
val name: String

def hello(): Unit
}

object Test {
def main(args: Array[String]): Unit = {
val person = new Person {
override val name: String = "teacher"

override def hello(): Unit = println("hello teacher")
}
}
}

单例对象(伴生对象)

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

单例对象语法

基本语法:

1
2
3
object Person{
val country:String="China"
}

说明

  1. 单例对象采用 object 关键字声明
  2. 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
  3. 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//(1)伴生对象采用 object 关键字声明
object Person {
var country: String = "China"
}

//(2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class Person {
var name: String = "bobo"
}

object Test {
def main(args: Array[String]): Unit = {
//(3)伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
println(Person.country)
}
}

apply 方法

说明

  1. 通过伴生对象的 apply 方法,实现不使用 new 方法创建对象。
  2. 如果想让主构造器变成私有的,可以在()之前加上 private。
  3. apply 方法可以重载。
  4. Scala 中 obj(arg)的语句实际是在调用该对象的 apply 方法,即 obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
  5. 当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的 apply 方法。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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)
}

//注意:也可以创建其它类型对象,并不一定是伴生类对象
}

扩展:在 Scala 中实现单例模式

特质(Trait)

Scala 语言中,采用特质 trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明。
Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。
Scala 引入 trait 特征,第一可以替代 Java 的接口,第二个也是对单继承机制的一种补充。

特质声明

  1. 基本语法

    1
    2
    3
    trait 特质名 {
    trait 主体
    }
  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    trait PersonTrait {
    // 声明属性
    var name: String = _

    // 声明方法
    def eat(): Unit = {
    }

    // 抽象属性
    var age: Int

    // 抽象方法
    def say(): Unit
    }

    //通过查看字节码,可以看到特质=抽象类+接口

特质基本语法

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

  1. 基本语法:

    1
    2
    没有父类: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. 动态混入:可灵活的扩展类的功能
      1. 动态混入:创建对象时混入 trait,而无需使类混入该 trait
      2. 如果混入的 trait 中有未实现的方法,则需要实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    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)中重写冲突方法。

image-20211109112815054

第二种,一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且两个 trait 继承自相同的 trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。

image-20211109112825806

所谓的特质叠加,就是将混入的多个 trait 中的冲突方法叠加起来,案例如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
trait Ball {
def describe(): String = {
"ball"
}
}

trait Color extends Ball {
override def describe(): String = {
"blue-" + super.describe()
}
}

trait Category extends Ball {
override def describe(): String = {
"foot-" + super.describe()
}
}

class MyBall extends Category with Color {
override def describe(): String = {
"my ball is a " + super.describe()
}
}

object TestTrait {
def main(args: Array[String]): Unit = {
println(new MyBall().describe())
}
}

特质叠加执行顺序

思考:上述案例中的 super.describe()调用的是父 trait 中的方法吗?

当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的 super.describe()调用的实际上是排好序后的下一个特质中的 describe()方法。,排序规则如下:

类的定义:

class MyBall extends Category with Color

1
2
叠加顺序  
MyClass Color Category Ball

第一步:列出混入的第一个特质(Category)的继承关系,作为临时叠加顺序

1
Category Ball

第二步:列出混入的第二个特质(Color)的继承关系,并将该顺序叠加到临时顺序前边,已经出现的特质不再重复

1
Color Ball

第三步:将子类(MyBall)放在临时叠加顺序的第一个,得到最终的叠加顺序

1
MyClass

结论:

  1. 案例中的 super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass 中的 super 指代 Color,Color 中的 super 指代 Category,Category 中的 super指代 Ball。
  2. 如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()。

特质自身类型

  1. 说明

    自身类型可实现依赖注入的功能。

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class User(val name: String, val age: Int)

    trait Dao {
    def insert(user: User) = {
    println("insert into database :" + user.name)
    }
    }

    trait APP {
    _: Dao =>
    def login(user: User): Unit = {
    println("login :" + user.name)
    insert(user)
    }
    }

    object MyApp extends APP with Dao {
    def main(args: Array[String]): Unit = {
    login(new User("bobo", 11))
    }
    }

特质和抽象类的区别

1
2
1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行(有无参构造)。

扩展

类型检查和转换

  1. 说明

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

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

    3. classOf 获取对象的类名。

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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)
    }
    }

枚举类和应用类

  1. 说明

    枚举类:需要继承 Enumeration

    应用类:需要继承 App

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    object Test {
    def main(args: Array[String]): Unit = {
    println(Color.RED)
    }
    }

    // 枚举类
    object Color extends Enumeration {
    val RED = Value(1, "red")
    val YELLOW = Value(2, "yellow")
    val BLUE = Value(3, "blue")
    }

    // 应用类
    object Test20 extends App {
    println("xxxxxxxxxxx");
    }

Type 定义新类型

  1. 说明

    使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    object Test {
    def main(args: Array[String]): Unit = {

    type S=String
    var v:S="abc"
    def test():S="xyz"
    } }

集合

集合简介

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

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

    不可变集合:scala.collection.immutable

    可变集合: scala.collection.mutable

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

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

建议:在操作集合的时候,不可变用符号,可变用方法

不可变集合继承图

image-20211109134822275

数组

不可变数组

第一种方式定义数组

定义:val arr1 = new ArrayInt

  1. new 是关键字
  2. [Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any
  3. (10),表示数组的大小,确定后就不可以变化

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
object TestArray {

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

//(1)数组定义
val arr01 = new Array[Int](4)
println(arr01.length) // 4

//(2)数组赋值
//(2.1)修改某个元素的值
arr01(3) = 10
//(2.2)采用方法的形式给数组赋值
arr01.update(0, 1)

//(3)遍历数组
//(3.1)查看数组
println(arr01.mkString(","))

//(3.2) 普 通 遍 历
for (i <- arr01) {
println(i)
}

//(3.3)简化遍历
def printx(elem: Int): Unit = {
println(elem)
}

arr01.foreach(printx)
// arr01.foreach((x)=>{println(x)})
// arr01.foreach(println(_)) arr01.foreach(println)

//(4)增加元素(由于创建的是不可变数组,增加元素,其实是产生新的数组)
println(arr01)
val ints: Array[Int] = arr01 :+ 5
println(ints)

}
}

第二种方式定义数组

val arr1 = Array(1, 2)

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

  2. 使用apply 方法创建数组对象

案例实操

1
2
3
4
5
6
7
8
9
10
object TestArray {

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

var arr02 = Array(1, 3, "bobo") println (arr02.length)
for (i <- arr02) {
println(i)
}
}
}

可变数组

  1. 定义变长数组

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

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

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

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

  2. 案例实操

    1. ArrayBuffer 是有序的集合

    2. 增加元素使用的是 append 方法(),支持可变参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import scala.collection.mutable.ArrayBuffer

    object TestArrayBuffer {
    def main(args: Array[String]): Unit = {
    //(1)创建并初始赋值可变数组
    val arr01 = ArrayBuffer[Any](1, 2, 3)
    //(2)遍历数组
    for (i <- arr01) {
    println(i)
    }
    println(arr01.length) // 3
    println("arr01.hash=" + arr01.hashCode())
    //(3)增加元素
    //(3.1)追加数据
    arr01.+=(4)
    //(3.2)向数组最后追加数据
    arr01.append(5, 6)
    //(3.3)向指定的位置插入数据
    arr01.insert(0, 7, 8)
    println("arr01.hash=" + arr01.hashCode())
    //(4)修改元素
    arr01(1) = 9 //修改第 2 个元素的值
    println("--------------------------")
    for (i <- arr01) {
    println(i)
    }
    println(arr01.length) // 5
    }
    }

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

说明

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

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

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

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

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
object TestArrayBuffer {
def main(args: Array[String]): Unit = {
//(1)创建一个空的可变数组
val arr2 = ArrayBuffer[Int]()
//(2)追加值
arr2.append(1, 2, 3)
println(arr2) // 1,2,3
//(3)ArrayBuffer ==> Array
//(3.1)arr2.toArray 返回的结果是一个新的定长数组集合
//(3.2)arr2 它没有变化
val newArr = arr2.toArray
println(newArr)

//(4)Array ===> ArrayBuffer
//(4.1)newArr.toBuffer 返回一个变长数组 newArr2
//(4.2)newArr 没有任何变化,依然是定长数组
val newArr2 = newArr.toBuffer
newArr2.append(123)
println(newArr2)
}
}

多维数组

  1. 多维数组定义

    val arr = Array.ofDimDouble

    说明:二维数组中有三个一维数组,每个一维数组中有四个元素

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    object DimArray {
    def main(args: Array[String]): Unit = {

    //(1)创建了一个二维数组, 有三个元素,每个元素是,含有 4 个元素一维数组()
    val arr = Array.ofDim[Int](3, 4)
    arr(1)(2) = 88
    //(2)遍历二维数组
    for (i <- arr) { //i 就是一维数组
    for (j <- i) {
    print(j + " ")
    }
    println()
    }
    }
    }

列表 List

不可变 List

  1. 说明

    1. List 默认为不可变集合

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

    3. 遍历 List

    4. List 增加数据

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

    6. 取指定数据

    7. 空集合 Nil

  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    object TestList {
    def main(args: Array[String]): Unit = {
    //(1)List 默认为不可变集合
    //(2)创建一个 List(数据有顺序,可重复)
    val list: List[Int] = List(1, 2, 3, 4, 3)

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

可变 ListBuffer

  1. 说明

    1. 创建一个可变集合 ListBuffer
    2. 向集合中添加数据
    3. 打印集合数据
  2. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import scala.collection.mutable.ListBuffer

    object TestList {
    def main(args: Array[String]): Unit = {
    //(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)
    }
    }

Set 集合

默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set 包

不可变 Set

说明

  1. Set 默认是不可变集合,数据无序
  2. 数据不可重复
  3. 遍历集合

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
object TestSet {
def main(args: Array[String]): Unit = {
//(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)
}
}
}

可变 mutable.Set

说明

  1. 创建可变集合 mutable.Set
  2. 打印集合
  3. 集合添加元素
  4. 向集合中添加元素,返回一个新的 Set
  5. 删除数据

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object TestSet {
def main(args: Array[String]): Unit = {
//(1)创建可变集合
val set = mutable.Set(1, 2, 3, 4, 5, 6)
//(3)集合添加元素
set += 8
//(4)向集合中添加元素,返回一个新的 Set
val ints = set.+(9)
println(ints)
println("set2=" + set)
//(5)删除数据
set -= (5)
//(2)打印集合
set.foreach(println)
println(set.mkString(","))
}
}

Map 集合

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

不可变 Map

说明

  1. 创建不可变集合 Map
  2. 循环打印
  3. 访问数据
  4. 如果 key 不存在,返回 0

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
object TestMap {
def main(args: Array[String]): Unit = {
// Map
//(1)创建不可变集合 Map
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
//(3)访问数据
for (elem <- map.keys) {
// 使用 get 访问 map 集合的数据,会返回特殊类型 Option(选项):有值(Some ),无值(None)
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

说明

  1. 创建可变集合
  2. 打印集合
  3. 向集合增加数据
  4. 删除数据
  5. 修改数据

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
object TestSet {
def main(args: Array[String]): Unit = {
//(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)
})
}
}

元组

说明

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。

注意:元组中最大只能有 22 个元素。

案例实操

  1. 声明元组的方式:(元素 1,元素 2,元素 3)
  2. 访问元组
  3. Map 中的键值对其实就是元组,只不过元组的元素个数为 2,称之为对偶
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
object TestTuple {
def main(args: Array[String]): Unit = {
//(1)声明元组的方式:(元素 1,元素 2,元素 3)
val tuple: (Int, String, Boolean) = (40, "bobo", true)
//(2)访问元组
//(2.1)通过元素的顺序进行访问,调用方式:_顺序号
println(tuple._1)
println(tuple._2)
println(tuple._3)
//(2.2)通过索引访问数据
println(tuple.productElement(0))
//(2.3)通过迭代器访问数据
for (elem <- tuple.productIterator) {
println(elem)
}
//(3)Map 中的键值对其实就是元组,只不过元组的元素个数为 2,称之为对偶
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
val map1 = Map(("a", 1), ("b", 2), ("c", 3))
map.foreach(tuple => {
println(tuple._1 + "=" + tuple._2)
})
}
}

集合常用函数

基本属性和常用操作

说明

  1. 获取集合长度

  2. 获取集合大小

  3. 循环遍历

  4. 迭代器

  5. 生成字符串

  6. 是否包含

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
object TestList {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
//(1)获取集合长度
println(list.length)
//(2)获取集合大小,等同于 length
println(list.size)
//(3)循环遍历
list.foreach(println)
//(4)迭代器
for (elem <- list.itera tor) {
println(elem)
}
//(5)生成字符串
println(list.mkString(","))
//(6)是否包含
println(list.contains(3))
}
}

衍生集合

说明

  1. 获取集合的头

  2. 获取集合的尾(不是头的就是尾)

  3. 集合最后一个数据

  4. 集合初始数据(不包含最后一个)

  5. 反转

  6. 取前(后)n 个元素

  7. 去掉前(后)n 个元素

  8. 并集

  9. 交集

  10. 差集

  11. 拉链

  12. 滑窗

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
object TestList {
def main(args: Array[String]): Unit = {
val list1: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
val list2: List[Int] = List(4, 5, 6, 7, 8, 9, 10)
//(1)获取集合的头
println(list1.head)
//(2)获取集合的尾(不是头的就是尾)
println(list1.tail)
//(3)集合最后一个数据
println(list1.last)
//(4)集合初始数据(不包含最后一个)
println(list1.init)
//(5)反转
println(list1.reverse)
//(6)取前(后)n 个元素
println(list1.take(3))
println(list1.takeRight(3))
//(7)去掉前(后)n 个元素
println(list1.drop(3))
println(list1.dropRight(3))
//(8)并集
println(list1.union(list2))
//(9)交集
println(list1.intersect(list2))
//(10)差集
println(list1.diff(list2))
//(11)拉链 注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
println(list1.zip(list2))
//(12)滑窗
list1.sliding(2, 5).foreach(println)
}
}

集合计算简单函数

说明

  1. 求和

  2. 求乘积

  3. 最大值

  4. 最小值

  5. 排序

实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
object TestList {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
//(1)求和
println(list.sum)
//(2)求乘积
println(list.product)
//(3)最大值
println(list.max)
//(4)最小值
println(list.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))
}
}
  1. sorted

    对一个集合进行自然排序,通过传递隐式的 Ordering

  2. sortBy

    对一个属性或多个属性进行排序,通过它的类型。

  3. sortWith

    基于函数的排序,通过一个 comparator 函数,实现自定义排序的逻辑。

集合计算高级函数

说明

  1. 过滤

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

  2. 转化/映射(map)

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

  3. 扁平化

  4. 扁平化+映射

    注:flatMap 相当于先进行 map 操作,在进行 flatten 操作集合中的每个元素的子元素映射到某个函数并返回新集合

  5. 分组(group)
    按照指定的规则对集合的元素进行分组

  6. 简化(归约)

  7. 折叠

实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
object TestList {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4,
5, 6), List(7, 8, 9))
val wordList: List[String] = List("hello world", "helloatguigu", " hello scala")
//(1)过滤
println(list.filter(x => x % 2 == 0))
//(2)转化/映射
println(list.map(x => x + 1))
//(3)扁平化
println(nestedList.flatten)
//(4)扁平化+映射 注:flatMap 相当于先进行 map 操作,在进行 flatten操作
println(wordList.flatMap(x => x.split(" ")))
//(5)分组
println(list.groupBy(x => x % 2))
}
}

Reduce 方法

Reduce 简化(归约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
object TestReduce {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4)
// 将数据两两结合,实现运算规则
val i: Int = list.reduce((x, y) => x - y)
println("i = " + i)
// 从源码的角度,reduce 底层调用的其实就是 reduceLeft
//val i1 = list.reduceLeft((x,y) => x-y)
// ((4-3)-2-1) = -2
val i2 = list.reduceRight((x, y) => x - y)
println(i2)
}
}

Fold 方法

Fold 折叠:化简的一种特殊情况。

案例实操:fold 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
object TestFold {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4)
// fold 方法使用了函数柯里化,存在两个参数列表
// 第一个参数列表为 : 零值(初始值)
// 第二个参数列表为: 简化规则
// fold 底层其实为 foldLeft
val i = list.foldLeft(1)((x, y) => x - y)
val i1 = list.foldRight(10)((x, y) => x - y)
println(i)
println(i1)
}
}

案例实操:两个集合合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object TestFold {
def main(args: Array[String]): Unit = {
// 两个 Map 的数据合并
val map1 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
val map2 = mutable.Map("a" -> 4, "b" -> 5, "d" -> 6)
val map3: mutable.Map[String, Int] = map2.foldLeft(map1) {
(map, kv) => {
val k = kv._1
val v = kv._2
map(k) = map.getOrElse(k, 0) + v
map
}
}
println(map3)
}
}

普通 WordCount 案例

  1. 需求

    单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果

  2. 需求分析

    image-20211109142420112

  3. 案例实操

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    object TestWordCount {

    def main(args: Array[String]): Unit = {
    // 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
    val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello")

    // 1) 将每一个字符串转换成一个一个单词
    val wordList: List[String] = stringList.flatMap(str => str.split(" "))
    //println(wordList)

    // 2) 将相同的单词放置在一起
    val wordToWordsMap: Map[String, List[String]] = wordList.groupBy(word => word)
    //println(wordToWordsMap)

    // 3) 对相同的单词进行计数
    // (word, list) => (word, count)
    val wordToCountMap: Map[String, Int] = wordToWordsMap.map(tuple => (tuple._1, tuple._2.size))

    // 4) 对计数完成后的结果进行排序(降序)
    val sortList: List[(String, Int)] = wordToCountMap.toList.sortWith {
    (left, right) => {
    left._2 > right._2
    }
    }

    // 5) 对排序后的结果取前 3 名
    val resultList: List[(String, Int)] = sortList.take(3)

    println(resultList)
    }
    }
    1. 方式一

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      object TestWordCount {
      def main(args: Array[String]): Unit = {
      // 第一种方式(不通用)
      val tupleList = List(("Hello Scala Spark World ", 4), ("Hello Scala Spark", 3), (" Hello Scala", 2), ("Hello", 1))

      val stringList: List[String] = tupleList.map(t => (t._1 + "") * t._2)
      //val words: List[String] =
      stringList.flatMap(s => s.split(" "))
      val words: List[String] = stringList.flatMap(_.split(" "))
      //在 map 中,如果传进来什么就返回什么,不要用_省略
      val groupMap: Map[String, List[String]] =
      words.groupBy(word => word)
      //val groupMap: Map[String, List[String]] =
      words.groupBy(_)
      // (word, list) => (word, count)
      val wordToCount: Map[String, Int] = groupMap.map(t => (t._1,
      t._2.size))
      val wordCountList: List[(String, Int)] =
      wordToCount.toList.sortWith {
      (left, right) => {
      left._2 > right._2
      }
      }.take(3)
      //tupleList.map(t=>(t._1 + " ") * t._2).flatMap(_.split("
      ")).groupBy(word=>word).map(t=>(t._1, t._2.size))
      println(wordCountList)
      }
      }
    2. 方式二

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      object TestWordCount {
      def main(args: Array[String]): Unit = {
      val tuples = List(("Hello Scala Spark World", 4), ("Hello Scala Spark", 3), (" Hello Scala", 2), ("Hello", 1))
      // (Hello,4),(Scala,4),(Spark,4),(World,4)
      // (Hello,3),(Scala,3),(Spark,3)
      // (Hello,2),(Scala,2)
      // (Hello,1)
      val wordToCountList: List[(String, Int)] = tuples.flatMap {
      t => {
      val strings: Array[String] = t._1.split(" ")
      strings.map(word => (word, t._2))
      }
      }
      // Hello, List((Hello,4), (Hello,3), (Hello,2), (Hello,1))
      // Scala, List((Scala,4), (Scala,3), (Scala,2)
      // Spark, List((Spark,4), (Spark,3)
      // Word, List((Word,4))
      val wordToTupleMap: Map[String, List[(String, Int)]] =
      wordToCountList.groupBy(t => t._1)
      val stringToInts: Map[String, List[Int]] =
      wordToTupleMap.mapValues {
      datas => datas.map(t => t._2)
      }
      stringToInts
      /*
      val wordToCountMap: Map[String, List[Int]] =
      wordToTupleMap.map {
      t => {
      (t._1, t._2.map(t1 => t1._2))
      }
      }
      val wordToTotalCountMap: Map[String, Int] =
      wordToCountMap.map(t=>(t._1, t._2.sum))
      println(wordToTotalCountMap)
      */
      }
      }

队列

说明

Scala 也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为 enqueue 和 dequeue。

案例实操

1
2
3
4
5
6
7
8
9
object TestQueue {
def main(args: Array[String]): Unit = {
val que = new mutable.Queue[String]()
que.enqueue("a", "b", "c")
println(que.dequeue())
println(que.dequeue())
println(que.dequeue())
}
}

并行集合

说明

Scala 为了充分使用多核 CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
object TestPar {
def main(args: Array[String]): Unit = {
val result1 = (0 to 100).map { case _ =>
Thread.currentThread.getName
}
val result2 = (0 to 100).par.map { case _ =>
Thread.currentThread.getName
}
println(result1)
println(result2)
}
}

模式匹配

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int i = 10
switch(i) {
case 10:
System.out.println("10");
break;
case 20:
System.out.println
("20");
break;
default:
System.out.println
("other number");
break;
}

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

基本语法

模式匹配语法中,采用 match 关键字声明,每个分支采用 case 关键字进行声明,当需要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _分支,类似于 Java 中 default 语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object TestMatchCase {
def main(args: Array[String]): Unit = {
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. 如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句,若此时没有 case _ 分支,那么会抛出 MatchError。
  2. 每个 case 中,不需要使用 break 语句,自动中断 case。
  3. match case 语句可以匹配任何类型,而不只是字面量。
  4. => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括

模式守卫

说明

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

案例实操

1
2
3
4
5
6
7
8
9
10
11
object TestMatchGuard {
def main(args: Array[String]): Unit = {
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))
}
}

模式匹配类型

匹配常量

说明

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

实操

1
2
3
4
5
6
7
8
9
10
11
12
object TestMatchVal {
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],也可使
用模式匹配实现同样的功能。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
object TestMatchClass {
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")))
}
}

匹配数组

说明

scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为 0 的数组。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object TestMatchArray {
def main(args: Array[String]): Unit = {
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)
}
}
}

匹配列表

方式一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object TestMatchList {
def main(args: Array[String]): Unit = {
//list 是一个存放 List 集合的数组
//请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1,
0, 0), List(88))) {
val result = list match {
case List(0) => "0" //匹配 List(0)
case List(x, y) => x + "," + y //匹配有两个元素的 List
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
}
}

方式二

1
2
3
4
5
6
7
8
9
10
object TestMatchList {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
case first :: second :: rest => println(first + "-" +
second + "-" + rest)
case _ => println("something else")
}
}
}

匹配元组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object TestMatchTuple {
def main(args: Array[String]): Unit = {
//对一个元组集合进行遍历
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
case (0, _) => "0 ..." //是第一个元素是 0 的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是 0 的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
}
}

扩展案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
object TestGeneric {
def main(args: Array[String]): Unit = {
//特殊的模式匹配 1 打印元组第一个元素
for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(elem._1)
}
for ((word, count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(word)
}
for ((word, _) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(word)
}
for (("a", count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
println(count)
}
println("--------------")
//特殊的模式匹配 2 给元组元素命名
var (id, name, age): (Int, String, Int) = (100, "zs", 20)
println((id, name, age))
println("--------------")
//特殊的模式匹配 3 遍历集合中的元组,给 count * 2
var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
//println(list.map(t => (t._1, t._2 * 2)))
println(
list.map {
case (word, count) => (word, count * 2)
}
)
var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
println(
list1.map {
case (groupkey, (word, count)) => (word, count * 2)
}
)
}
}

匹配对象及样例类

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class User(val name: String, val age: Int)

object User {
def apply(name: String, age: Int): User = new User(name, age)

def unapply(user: User): Option[(String, Int)] = {
if (user == null)
None
else
Some(user.name, user.age)
}
}

object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}

小结

  • val user = User(“zhangsan”,11),该语句在执行时,实际调用的是 User 伴生对象中的apply 方法,因此不用 new 关键字就能构造出相应的对象。

  • 当将 User(“zhangsan”, 11)写在 case 后时[case User(“zhangsan”, 11) => “yes”],会默认调用 unapply 方法(对象提取器),user 作为 unapply 方法的参数,unapply 方法将 user 对象的 name 和 age 属性提取出来,与 User(“zhangsan”, 11)中的属性值进行匹配

  • case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,属性不一致,或返回 None,则匹配失败。

  • 若只提取对象的一个属性,则提取器为 unapply(obj:Obj):Option[T]

    若提取对象的多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]

    若提取对象的可变个属性,则提取器为 unapplySeq(obj:Obj):Option[Seq[T]]

样例类

  1. 语法:

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

  2. 说明

    • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCode 和 copy。
    • 2 样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。
    • 3 构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)

实操

上述匹配对象的案例使用样例类会节省大量代码

1
2
3
4
5
6
7
8
9
10
11
12
case class User(name: String, age: Int)

object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}

变量声明中的模式匹配

1
2
3
4
5
6
7
8
9
10
11
12
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) = Person1("zhangsan", 16)
println(s"name=$name,age=$age")
}
}

for 表达式中的模式匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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) //3 个
}
println("----------------------")
//遍历 value=0 的 k-v ,如果 v 不是 0,过滤
for ((k, 0) <- map) {
println(k + " --> " + 0) // B->0
}
println("----------------------")
//if v == 0 是一个过滤的条件
for ((k, v) <- map if v >= 1) {
println(k + " ---> " + v) // A->1 和 c->33
}
}
}

偏函数中的模式匹配(了解)

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式匹配实现的。

偏函数定义

1
2
3
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}

image-20211109144508564

注:该偏函数的功能是返回输入的 List 集合的第二个元素

偏函数原理

上述代码会被 scala 编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为 Boolean。

1
2
3
4
5
6
7
8
9
10
11
12
val second = new PartialFunction[List[Int], Option[Int]] {
//检查输入参数是否合格
override def isDefinedAt(list: List[Int]): Boolean = list match
{
case x :: y :: _ => true
case _ => false
}
//执行函数逻辑
override def apply(list: List[Int]): Option[Int] = list match
{
case x :: y :: _ => Some(y)
} }

偏函数使用

偏函数不能像 second(List(1,2,3))这样直接使用,因为这样会直接调用 apply 方法,而应该调用 applyOrElse 方法,如下

1
second.applyOrElse(List(1,2,3), (_: List[Int]) => None)

applyOrElse 方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即 isDefinedAt 返回 true,则执行 apply 方法,否则执行 defalut 方法,default 方法为参数不满足要求的处理逻辑。

案例实操

  1. 需求

    将该 List(1,2,3,4,5,6,”test”)中的 Int 类型的元素加一,并去掉字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5, 6, "test")
    val list1 = list.map { a =>
    a match {
    case i: Int => i + 1
    case s: String => s + 1
    }
    }
    println(list1.filter(a => a.isInstanceOf[Int]))
    }
  2. 实操

    方法一:

    List(1,2,3,4,5,6,”test”).filter(.isInstanceOf[Int]).map(.asInstanceOf[Int] + 1).foreach(println)

    方法二:

    List(1, 2, 3, 4, 5, 6, “test”).collect { case x: Int => x + 1 }.foreach(println)

异常

语法处理上和 Java 类似,但是又不尽相同。

Java 异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ExceptionDemo {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
int c = a / b;
} catch (ArithmeticException e) {
// catch 时,需要将范围小的写到前面
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("finally");
}
}
}

注意事项

  1. Java 语言按照 try—catch—finally 的方式来处理异常
  2. 不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资源。
  3. 可以有多个 catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。

Scala 异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def main(args: Array[String]): Unit = {
try {
var n = 10 / 0
} catch {
case ex: ArithmeticException => {
// 发生算术异常
println("发生算术异常")
}
case ex: Exception => {
// 对异常处理
println("发生了异常 1")
println("发生了异常 2")
}
} finally {
println("finally")
}
}
  1. 我们将可疑代码封装在 try 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。如果发生任何异常,catch 处理程序将处理它,程序将不会异常终止。

  2. Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)”异常, 即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。

  3. 异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在 Scala 中也不会报错,但这样是非常不好的编程风格。

  4. finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和 Java 一样。

  5. 用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing,因为 Nothing 是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方

    1
    2
    3
    def test():Nothing = {
    throw new Exception("不对")
    }
  6. java 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch块中,以避免程序异常终止。在 Scala 中,可以使用 throws 注解来声明异常

    1
    2
    3
    4
    5
    6
    7
    def main(args: Array[String]): Unit = {
    f11()
    }
    @throws(classOf[NumberFormatException])
    def f11()={
    "abc".toInt
    }

隐式转换

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译

隐式函数

说明

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

案例实操

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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))
}
}

隐式参数

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

说明

  1. 同一个作用域中,相同类型的隐式值只能有一个
  2. 编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
  3. 隐式参数优先于默认参数

案例实操

1
2
3
4
5
6
7
8
9
10
11
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
}
}

隐式类

在 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。

隐式类说明

  1. 其所带的构造参数有且只能有一个
  2. 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object TestImplicitClass {

implicit class MyRichInt(arg: Int) {
def myMax(i: Int): Int = {
if (arg < i) i else arg
}

def myMin(i: Int) = {
if (arg < i) arg else i
}
}

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

隐式解析机制

说明

  1. 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)
  2. 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象。

案例实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.atguigu.chapter10

import com.atguigu.chapter10.Scala05_Transform4.Teacher
//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,

object TestTransform extends PersonTrait {
def main(args: Array[String]): Unit = {
//(1)首先会在当前代码作用域下查找隐式实体
val teacher = new Teacher()
teacher.eat()
teacher.say()
}

class Teacher {
def eat(): Unit = {
println("eat...")
}
}

}

trait PersonTrait {
}

object PersonTrait {

// 隐式类 : 类型 1 => 类型 2
implicit class Person5(user: Teacher) {
def say(): Unit = {
println("say...")
}
}

}

泛型

协变和逆变

语法

1
2
3
4
5
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变

说明

协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。

逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。

不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。

实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//泛型模板
//class MyList<T>{}
//不变
//class MyList[T]{}
//协变
//class MyList[+T]{}
//逆变
//class MyList[-T]{}
class Parent {}

class Child extends Parent {}

class SubChild extends Child {}

object Scala_TestGeneric {
def main(args: Array[String]): Unit = {
//var s:MyList[Child] = new MyList[SubChild]

}
}

泛型上下限

语法

1
2
3
4
Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}

说明

泛型的上下限的作用是对传入的泛型进行限定。

实操

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Parent {}

class Child extends Parent {}

class SubChild extends Child {}

object Scala_TestGeneric {
def main(args: Array[String]): Unit = {
//test(classOf[SubChild])
//test[Child](new SubChild)
}

//泛型通配符之上限
//def test[A <: Child](a:Class[A]): Unit ={
// println(a)
//}
//泛型通配符之下限
//def test[A >: Child](a:Class[A]): Unit ={
// println(a)
//}
//泛型通配符之下限 形式扩展
def test[A >: Child](a: A): Unit = {
println(a.getClass.getName)
}
}

上下文限定

语法

def f[A : B](a: A) = println(a) //等同于 def fA(implicit arg:B[A])=println(a)

说明

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

1
2
3
implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]

实操

1
2
def f[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)

总结

开发环境

要求掌握必要的 Scala 开发环境搭建技能。

变量和数据类型

掌握 var 和 val 的区别

掌握数值类型(Byte、Short、Int、Long、Float、Double、Char)之间的转换关系

流程控制

掌握 if-else、for、while 等必要的流程控制结构,掌握如何实现 break、continue 的功能。

函数式编程

掌握高阶函数、匿名函数、函数柯里化、闭包、函数参数以及函数至简原则。

面向对象

掌握 Scala 与 Java 继承方面的区别、单例对象(伴生对象)、构造方法、特质的用法及功能。

集合

掌握常用集合的使用、集合常用的计算函数。

模式匹配

掌握模式匹配的用法

下划线

掌握下划线不同场合的不同用法

异常

掌握异常常用操作即可

隐式转换

掌握隐式方法、隐式参数、隐式类,以及隐式解析机制

泛型

掌握泛型语法