Scala 基础

Scala 基础

简介

  • Scala 是一门基于 jvm 的多范式的编程语言,由 Martin Odersky 创建, 2003 年首次发布;

  • Scala设计的初衷是实现可伸缩 、并集成面向对象和函数式编程的各种特性;

  • Scala 平滑地集成面向对象和函数式语言的特点,并且 Scala 被编译在 Java 虚拟机上运行;

特性

  • 面向对象:Scala 的每一个值都是一个对象,是纯面向对象语言,使用组合机制代替继承;

  • 函数式:Scala 的每一个函数为一个值,提供了一个轻量级的语法用来定义匿名函数,支持高阶函数,它允许函数嵌套,并支持 curry 处理。

  • 静态类型:

  • 基于 JVM:Scala 可以使用在 Java SDK 的 Scala 中的所有类,也是自定义 Java 类或者 Java 开源项目;

语法

运算符

Scala 含有丰富的内置运算符,包括以下几种类型:

  • 算术运算符:+-*/%
  • 关系运算符:==!=><>=<=
  • 逻辑运算符:&&||!
  • 位运算符:~&|^<<>>>>> (无符号右移)
  • 赋值运算符:=

数据类型

基本类型

Scala 中的基本类型和 Java 中的基本类型对应:

Java 基本类型Java 类Scala 类型说明
byteByteByte-128~127
shortShortShort-32,768~32,767
intIntInt-2,147,483,648~2,147,483,647
longLongLong-9,223,372,036,854,775,808~9,223,372,036,854,775,807
floatFloatFloatIEEE 定义的 32 位浮点数
doubleDoubleDoubleIEEE 定义的 64 位浮点数
charCharacterChar一个 Unicode 字符
booleanBooleanBooleantrue 或者 false

特殊类型

Scala 中有几个特殊的类型:

  • Any:任意类型,是所有类型的父类;

  • AnyVal:值类型,它的构造函数有一个参数,Scala 编译器对其做了一些特殊处理;

  • AnyRef:表示所有引用类型,也就是除了继承了AnyVal的类以外的所有类的父类,类似于 Java 中的Object

  • Unit:无类型,类似于 Java 中的void,但是在 Scala 中,可以定义 Unit 类型的变量。Unit类型是单值类型(比较:Boolean类型是双值类型,有truefalse两个取值)。

  • Nothing:是所有类型的子类,基本上只用来作为泛型的参数。

  • Null是所有引用类型的子类,有一种值null

元组

  • 元组的类型表示是用()包含的一系列类型,值是用()包含的一系列表达式;

  • 长度为 1 的元组必须要用Tuple1声明;

  • 其他类型的元组都可以使用()表示;

1
2
3
val a: Tuple1[Int] = Tuple1(1)
val b: (Int, Int) = (1, 2)
val c: (Int, Int, Int) = (1, 2, 3)

变量

  • val:常量,类似于 Java 中的 final 变量,一旦初始化之后,不可以重新赋值(我们可以称它为常变量)

  • var:变量,类似于一般的非 final 变量,可以任意重新赋值;

1
val msg = "Hello,World"

函数和方法

  • 有函数和方法;

  • 方法:使用def定义,一个具有名称和签名的类的一部分;

  • 函数:使用val定义,一个可以分配给变量的完整对象;

  • 使用def关键字定义;

1
2
3
4
5
6
7
8
9
def hello() = {"Hello World!"} //方法
val inc = (x:Int) => x + 1    // 函数
inc(7)
//
def add(x:Int,y:Int) = x+y
add(1,2)
// 科里化,将两个参数的函数变为一个参数的过程
def add(x:Int)(y:Int) = x + y
add(1)(2)    

类(class)

  • 类由字段方法组成;

  • 字段保存对象的状态,并使用valvar定义;

  • 方法完成对象的计算任务,并使用定义关键字def

  • private关键字添加到valvar字段,以防止 getter 和 setter 方法生成。

类构造函数

  • 类的定义中可以有 1 个主构造函数, 多个辅助构造函数

  • 主构造函数和类名称定义在一起;

  • 辅助构造函数通过 this 方法来定义;

  • 如果构造函数参数声明为 val,只为它生成一个 getter 方法;

  • 如果构造函数参数声明为 var,将生成访问器和 mutator 方法;

  • 当在构造函数参数上未指定 val 和 var 时,不生成 getter 或 setter;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Book class
class Book (var title :String, var ISBN: Int) {    //主构造函数
    // 辅助构造函数1
    def this(title: String) {
        this(title, 2222)
    }
    // 辅助构造函数2
    def this() {
        this("CSS")
        this.ISBN = 1111
    }
    override def toString = s"$title ISBN- $ISBN"
}

入口

  • 主函数有两种启动方式:

    • 继承 App

    • 实现main函数

1
2
3
4
5
6
7
8
9
object Test extends App {
  //ToDo
}

object Test{
  def main(args: Array[String]): Unit = {
    //ToDo
  }
}

Object(伴生对象)

  • scala 的class没有static关键字概念, scala使用与类同名的object来封装class的静态属性静态方法

  • 在Scala中,通过把变量放置在一个单例对象保管的方式,来实现Java的静态功能,这个专门保存单例对象的类在Scala中称之为伴生对象,它使用一个独立的object关键字来修饰,而非class

  • 伴生类和伴生对象必须存放在一个.scala文件中;

  • 当同一个文件内同时存在object xclass x的声明时:

    • 我们称class x称作object x伴生类

    • object x称作class x伴生对象

    • 其中伴生类和伴生对象需要同名。

    • 类和伴生对象之间没有界限——它们可以互相访问彼此的private字段和private方法;

  • 伴生对象中的属性可以直接使用类名进行调用;

  • 伴生类中的属性,需要先实例化对象,才能够进行调用;

  • 伴生对象特有的方法:apply。

  • 没有class,只有object则是单例模式类;

  • object 中所有成员变量和方法默认都是 static 的;

  • 伴生类object x被编译成了x.class,伴生对象object x被编译成了x$.class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class AssociatedDemo {
  val a:Int = 10;
  var b:Int = 2;
}

object AssociatedDemo{
  val object_v1:String = "变量1";

  def main(args: Array[String]): Unit = {
    // 注:只有伴生对象中可以定义主函数,类似于static修饰
    println("伴生对象变量:" + AssociatedDemo.object_v1);
    println("伴生类变量:" + (new AssociatedDemo()).a);
  }
}

apply

  • apply()称为注入方法,是一个语法糖
  • 直接调用类(对象)名称时候,默认执行的是该类(对象)的apply()方法;
  • 例如: Demo(“hello”) 实际调用的是 Demo.apply(“hello”), 因此 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
class AssociatedDemo {
  // class中的apply()方法
  def apply(param:String){
    println("class apply method called:" + param)
  }
}

object AssociatedDemo{
  // object中的apply()方法
  def apply(param:String){
    println("object apply method called:" + param)
  }

  def main(args: Array[String]): Unit = {
    // class 的apply()
    val ad2 = new AssociatedDemo();
    ad2("AAA")
    ad2("BBB")

    // object 的apply()
    AssociatedDemo("CCC")
    AssociatedDemo("DDD")
  }
}

Case Class(样例类)

  • Case Class(样例类)是一种特殊的类,能够被优化以用于模式匹配;

  • 类似于Java中的entity类,其属性不可变,且均为public;

  • Case Class在比较的时候是按值比较而非按引用比较;

当一个类被声名为 case class 的时候,scala 会帮助我们做下面几件事情:

  • 构造器中的参数默认的是 val 类型,除非显式声明为 var;

  • 自动创建伴生对象,并实现apply()方法。可以不直接显示地 new 对象;

  • 伴生对象自动实现unapply方法,应用于模式匹配;

  • 实现自己的 toString、hashCode、copy、equals 方法;

  • 对应所有构造参数,Scala 会自动定义对应的取值函数;

  • case class的实例可以借由模式匹配来拆解;

数据结构

Array

  • 长度不可改变的数组;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 数组元素求和
val a = Array(1, 2, 3, 4, 5)
val sum = a.sum
// 获取数组最大值
val max = a.max
// 对数组进行排序
scala.util.Sorting.quickSort(a)
// 获取数组中所有元素内容
a.mkString
a.mkString(", ")
a.mkString("<", ",", ">")
// toString函数
a.toString
b.toString

ArrayBuffer

  • 可变数组
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 如果不想每次都使用全限定名,则可以预先导入ArrayBuffer类
import scala.collection.mutable.ArrayBuffer
// 使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer
val b = ArrayBuffer[Int]()
// 使用+=操作符,可以添加一个元素,或者多个元素
// 这个语法必须要谨记在心!因为spark源码里大量使用了这种集合操作语法!
b += 1
b += (2, 3, 4, 5)
// 使用++=操作符,可以添加其他集合中的所有元素
b ++= Array(6, 7, 8, 9, 10)
// 使用trimEnd()函数,可以从尾部截断指定个数的元素
b.trimEnd(5)

Sealed(封闭)

  • sealed关键字可以修饰classtrait

  • 其修饰的 trait,class 只能在当前文件里面被继承

  • 在检查模式匹配的时候,用 sealed 修饰目的是让 scala 知道这些 case 的所有情况,scala 就能够在编译的时候进行检查,看你写的代码是否有没有漏掉什么没 case 到,减少编程的错误。

  • 参考

  1. https://docs.scala-lang.org/zh-cn/

  2. Scala 教程_w3cschool

  3. Scala 第一个 Scala 程序_w3cschool

updatedupdated2024-05-102024-05-10