3天极速掌握 Scala 语言:First Day

Posted Amo Xiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3天极速掌握 Scala 语言:First Day相关的知识,希望对你有一定的参考价值。

一、基础语法学习

1.1 声明变量

1.1.1 语法格式

在 Scala 中,可以使用 val 或者 var 关键字来定义变量,语法格式如下:

val/var 变量标识:变量类型 = 初始值

注意:

  1. val 定义的是 不可重新赋值的变量,不可变。 不是 java 中 final 的意思。Scala 也有 final,在 Scala 中 final 表示数值不可变以及不可被覆写。val 仅仅是数值不变,可以被复写。
  2. var 定义的是 可重新赋值的变量,可变。
  3. Scala 中定义变量类型写在变量名后面。
  4. Scala 的语句最后不需要添加分号。
  5. Scala 中类型是 首字母大写的。

1.1.2 在解释器中定义一个变量

示例:定义一个变量保存一个人的名字 “Amo”

步骤:

  1. 打开 Scala 解释器
  2. 定义一个字符串类型的变量用来保存名字

参考代码

scala> val name:String = "Amo"
name: String = Amo

1.1.3 val 和 var 变量

示例1

给名字变量进行重新赋值为 Jerry,观察其运行结果
在这里插入图片描述
示例2

使用 var 重新定义变量来保存名字 “Paul”,并尝试重新赋值为 “Ben”,观察其运行结果:

scala> var name:String = "Paul"
name: String = Paul

scala> name = "Ben"
name: String = Ben

优先使用 val 定义变量,如果变量需要被重新赋值,才使用 var。

1.1.4 使用类型推断来定义变量

Scala 的语法要比 Java 简洁,我们可以使用一种更简洁的方式来定义变量。Scala 是强类型语言,千万不要被它省略类型所迷惑。它是可以省略类型,不代表变量没有类型。

示例1

使用更简洁的语法定义一个变量保存一个人的名字 “Amo”

参考代码

scala> val name = "Amo"
name: String = Amo

Scala 可以自动根据变量的值来自动推断变量的类型,这样编写代码更加简洁。

1.1.5 惰性赋值

什么时候用到,什么时候被加载的内存中。 在企业的大数据开发中,有时候会编写非常复杂的 SQL 语句,这些 SQL 语句可能有几百行甚至上千行。这些 SQL 语句,如果直接加载到 JVM 中,会有很大的内存开销。如何解决?当有一些变量保存的数据较大时,但是不需要马上加载到 JVM 内存。可以使用 惰性赋值 来提高效率。语法格式:

lazy val 变量名 = 表达式

注意:var 不可以用 lazy
在这里插入图片描述

1.2 字符串

Scala 提供多种定义字符串的方式,将来我们可以根据需要来选择最方便的定义方式。使用双引号、使用插值表达式、使用三引号。

1.2.1 使用双引号

语法

val/var 变量名 = "字符串"

示例: 有一个人的名字叫 “Amo”,请打印他的名字以及名字的长度。

参考代码

scala> val name:String = "Amo"
name: String = Amo

scala> println(name + name.length)
Amo3

1.2.2 使用插值表达式

Scala 中,可以使用插值表达式来定义字符串,有效避免大量字符串的拼接。语法:

val/var 变量名 = s"${变量/表达式}字符串"
  1. 在定义字符串之前添加 s
  2. 在字符串中,可以使用 ${} 来引用变量或者编写表达式

示例: 请定义若干个变量,分别保存:“Amo”、18、“male”,定义一个字符串,保存这些信息。打印输出:name=Amo, age=18, sex=male

参考代码

scala> val name = "Amo"
name: String = Amo

scala> val age = 18
age: Int = 18

scala> val sex = "male"
sex: String = male

scala> println(s"name=${name},age=${age},sex=${sex}")
name=Amo,age=18,sex=male

1.2.3 使用三引号

如果有大段的文本需要保存,就可以使用三引号来定义字符串。例如:保存一大段的 SQL 语句。三个引号中间的所有字符串都将作为字符串的值。语法:

val/var 变量名 = """字符串1
字符串2"""

示例: 定义一个字符串,保存以下 SQL 语句。

scala> val sql = """
     | create table course(
     | cid string,
     | cname string,
     | tid string)
     | clustered by(cid) into 3 buckets
     | row format delimited fields terminated by '\\t';
     | """
sql: String =
"
create table course(
cid string,
cname string,
tid string)
clustered by(cid) into 3 buckets
row format delimited fields terminated by '\\t';
"

scala> println(sql)

create table course(
cid string,
cname string,
tid string)
clustered by(cid) into 3 buckets
row format delimited fields terminated by '\\t';

但是要注意一点,三引号字符串不要出现重复,比如 ""'' aaa """ bbb """。因为,Scala 编译器,从第一个三引号开始,到第二个三引号结束,算一个完整的字符串,后面的就是多出来的语法错误的内容。
在这里插入图片描述

1.3 数据类型与操作符

Scala 中的类型以及操作符绝大多数和 Java 一样,我们主要来学习,与 Java 不一样的一些用法,Scala 类型的继承体系。

1.3.1 数据类型

基础类型类型说明
Byte8位带符号整数
Short16位带符号整数
Int32位带符号整数
Long64位带符号整数
Char16位无符号Unicode字符
StringChar类型的序列(字符串)
Float32位单精度浮点数
Double64位双精度浮点数
Booleantrue或false

Scala 类型与 Java 的区别:

  1. Scala 中所有的类型都使用 大写字母 开头。
  2. 整型使用 Int 而不是 Integer。
  3. Scala 中定义变量可以不写类型,让 Scala 编译器自动推断。

1.3.2 运算符

类别操作符
算术运算符+、-、*、/、%(加减乘除和取模)
关系运算符>、<、==、!=、>=、<=
逻辑运算符&&、||、!
位运算符&、||、^、<<、>>

Scala 中的运算符, 基本上和 Java 一样, 除了:

  1. Scala 中没有,++、-- 运算符
  2. 与 Java 不一样,在 Scala 中,想要比较值是否相同,就用 == 即可。 在 Scala 中 == 是很智能的。 如果对象为 NULL 会自动判断空的情况, 如果对象不为空会自动判断对象的值是否相等。想要比较内存地址,使用 .eq 方法。

示例: 有一个字符串"abc",再创建第二个字符串,值为:在第一个字符串后拼接一个空字符串。然后使用比较这两个字符串是否相等、再查看它们的引用值是否相等。

参考代码

scala> val str1 = "abc"
str1: String = abc

scala> val str2 = str1 + ""
str2: String = abc

scala> str1==str2
res4: Boolean = true

scala> str1.eq(str2)
res5: Boolean = false

1.3.3 Scala 类型层次结构

image-20210316205637253所有的类型都是从 Any 继承了, 也就是我们 Scala 是一种单根继承体系。

类型说明
Any所有类型 的父类,它有两个子类 AnyRef 与 AnyVal
AnyVal所有数值类型 的父类
AnyRef所有对象类型(引用类型)的父类 (String 本质就是 Char 数组,也是引用类型哦)
Unit表示空,Unit 是 AnyVal 的子类,它只有一个的实例{% em %}() {% endem %}
它类似于 Java 中的 void,但 Scala 要比 Java 更加面向对象,Unit 本身也是一个类哦,当返回值为空的时候,返回的就是 Unit,Unit 的实例就是一个括号(). 这个对象用来做空返回值的时候用
NullNull 也就是 AnyRef 的子类,也就是说它是所有引用类型的子类。它的实例是{% em %}** null **{% endem %}
可以将null赋值给任何引用对象类型. 真的是空,表示的是引用类型指针指向的内存地址不存在或者不指向内存区域。
Nothing所有类型的 子类
不能直接创建该类型实例,某个方法抛出异常时,返回的就是 Nothing 类型,因为 Nothing 是所有类的子类,那么它可以赋值为任何类型

问题: 以下代码是否有问题?

val b:Int = null

Scala 会解释报错:Null 类型并不能转换为 Int 类型,说明 Null 类型并不是 Int 类型的子类,也就是不是数值类型 AnyVal 的子类。

1.4 条件表达式

条件表达式就是 if 表达式,if 表达式可以根据给定的条件是否满足,根据条件的结果(真或假)决定执行对应的操作。Scala 条件表达式的语法和 Java 一样。

1.4.1 有返回值的 if

与 Java 不一样的是:

  1. 在 Scala 中,条件表达式也是有返回值的。
  2. 在 Scala 中,没有三元表达式,可以使用 if 表达式替代三元表达式。

示例: 定义一个变量 sex,再定义一个 result 变量,如果 sex 等于 “male”,result 等于 1,否则 result 等于 0。

参考代码

scala> val sex = "male"
sex: String = male

scala> val result = if sex == "male" 1 else 0
<console>:1: error: '(' expected but identifier found.
       val result = if sex == "male" 1 else 0
                       ^

scala> val result = if(sex == "male") 1 else 0
result: Int = 1

1.4.2 块表达式

  1. Scala 中,使用 {} 表示一个块表达式。
  2. 和 if 表达式一样,块表达式也是有值的。
  3. 值就是最后一个表达式的值(因为 Scala 中没有 return 语句)

问题: 请问以下代码,变量 a 的值是什么?

scala> val a = {
     | println(1+1)
     | 1 + 1
     | }
2
a: Int = 2

1.5 循环

在 Scala 中,可以使用 for 和 while,但一般推荐使用 for 表达式,因为 for 表达式语法更简洁。

1.5.1 for 表达式

语法格式如下:

for(i <- 表达式/数组/集合) {
    // 表达式
}

示例: 简单循环,使用 for 表达式打印 1-10 的数字。

步骤:

  1. 生成 1-10 的数字(提示:使用 to 方法)。
  2. 使用 for 表达式遍历,打印每个数字。

参考代码1:

scala> val nums = 1.to(10)
nums: scala.collection.immutable.Range.Inclusive = Range 1 to 10
scala> for(i <- nums) println(i)

参考代码2:

scala> for(i <- 1 to 10) println(i)
1
2
3
4
5
6
7
8
9
10

scala> for(i <- 1 until 10) println(i)
1
2
3
4
5
6
7
8
9

嵌套循环: 使用 for 表达式,打印以下字符。

*****
*****
*****

参考代码

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

for(i <- 1 to 3){
 for(i <- 1 to 5){
   print("*")
 }
 println()
}

// Exiting paste mode, now interpreting.

*****
*****
*****

scala> for(i <- 1 to 3;j <- 1 to 5){print("*");if(j==5) println("*")}
******
******
******

scala>

也可以在一个 for 中通过 ; 来定义多层嵌套的 for 循环。

守卫: for 表达式中,可以添加 if 判断语句,这个 if 判断就称之为守卫。我们可以使用守卫让 for 表达式更简洁。

语法

for(i <- 表达式/数组/集合 if 表达式) {
    // 表达式
}

示例: 使用 for 表达式打印 1-10 之间能够整除 3 的数字。

参考代码:

// 添加守卫,打印能够整除3的数字
scala> for(i <- 1 to 10 if i % 3 == 0) println(i)
3
6
9

for 推导式: 将来可以使用 for 推导式生成一个新的集合(一组数据)。在 for 循环体中,可以使用 yield 表达式构建出一个集合,我们把使用 yield 的 for 表达式称之为推导式。

示例: 生成一个 10、20、30…100 的集合。

参考代码:

// for推导式:for表达式中以yield开始,该for表达式会构建出一个集合
scala> val v = for(i <- 1 to 10) yield i * 10
v: scala.collection.immutable.IndexedSeq[Int] = Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

yield 推导式就是将循环的每一个返回值都收集起来,收集到一个集合中作为 for 循环的返回值。

1.5.2 while 循环

Scala 中 while 循环和 Java 中是一致的。

示例: 打印 1-10 的数字。

参考代码

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

var i = 1
while(i <= 10){
 println(i)
 i += 1
}

// Exiting paste mode, now interpreting.

1
2
3
4
5
6
7
8
9
10
i: Int = 11

scala>

1.6 break 和 continue

在 Scala 中,类似 Java 和 C++ 的 break/continue 关键字被移除了。如果一定要使用 break/continue,就需要使用 scala.util.control 包的 Break 类的 breakablebreak 方法。

1.6.1 实现 break

用法

  1. 导入 Breaks 包 import scala.util.control.Breaks._
  2. 使用 breakable 将 for 表达式包起来。
  3. for表达式中需要退出循环的地方,添加 break() 方法调用。

示例: 使用 for 表达式打印 1-100 的数字,如果数字到达 50,退出 for 表达式。

参考代码

// 导入scala.util.control包下的Break
scala> import scala.util.control.Breaks._
import scala.util.control.Breaks._

scala> breakable{
     | for (i <- 1 to 100){
     |  if(i >= 50) break()
     |  else println(i)
     | }
     | }

1.6.2 实现 continue

用法: continue 的实现与 break 类似,但有一点不同:实现 break 是用 breakable{} 将整个 for 表达式包起来,而实现 continue 是用 breakable{} 将 for 表达式的循环体包含起来就可以了。

示例: 打印 1-100 的数字,使用 for 表达式来遍历,如果数字能整除10,不打印。

// 导入scala.util.control包下的Break    
scala> import scala.util.control.Breaks._
import scala.util.control.Breaks._

scala> for(i <- 1 to 100){
     | breakable{
     | if(i%10==0)break()
     | else println(i)
     | }
     | }

解答, 为何 break 功能要包住外面,continue 包住 for 循环体呢?因为,在这两种实现中,break 方法都是跳出了 breakable 包含的代码块,向下继续执行。唯一不同的是,如果 breakable 包住 for 循环,那么跳出的时候,就把 for 循环也跳出了,就向下继续执行了,就实现了类似 java 中 break 的功能。如果 breakable 包住 for 循环的循环体的话,那么跳出的时候,还在 for 循环内,只是当前这一次的循环体执行完成了,所以继续执行就是继续执行下一次的 for 循环。所以,包住的地方不同,实现的功能不同,但是对于 breakable 和 break 来说,他们的功能从未改变。

1.7 方法

一个类可以有自己的方法,Scala 中的方法和 Java 方法类似。但 Scala 与 Java 定义方法的语法是不一样的。

1.7.1 定义方法

语法

def methodName (参数名:参数类型, 参数名:参数类型) : [return type] = {
    // 方法体:一系列的代码
}
  1. 参数列表的参数类型不能省略(因为之前定义变量的时候可以省略,这里不要混淆了,因为变量给了初始值,可以根据初始值推断类型)。
  2. 返回值类型可以省略,由 Scala 编译器自动推断。
  3. 返回值可以不写 return,默认就是 {}块表达式的值。

示例:

  1. 定义一个方法,实现两个整形数值相加,返回相加后的结果。
  2. 调用该方法。

参考代码

scala> def add(a:Int, b:Int) = a + b
add: (a: Int, b: Int)Int

scala> add(5,6)
res4: Int = 11

1.7.2 返回值类型推断

Scala 定义方法可以省略返回值,由 Scala 自动推断返回值类型。这样方法定义后更加简洁。定义递归方法,不能省略返回值类型。因为,递归调用是一层一层向内走,当前那一层无法推断返回类型,会导致一系列问题。

示例: 定义递归方法(求阶乘),比如求 10 的阶乘,10 * 9 * 8 * 7 * 6 * ... * 1

参考代码
在这里插入图片描述

1.8 方法参数

Scala 中的方法参数,使用比较灵活。它支持以下几种类型的参数:默认参数、带名参数、变长参数。

1.8.1 默认参数

在定义方法时可以给参数定义一个默认值。

示例:

  1. 定义一个计算两个值相加的方法,这两个值默认为 0。
  2. 调用该方法,不传任何参数。

参考代码

// x,y带有默认值为0 
scala> def add(x:Int = 0, y:Int = 0) = x + y
add: (x: Int, y: Int)Int

scala> add()
res5: Int = 0

1.8.2 带名参数

在调用方法时,可以指定参数的名称来进行调用。

示例:

  1. 定义一个计算两个值相加的方法,这两个值默认为 0。
  2. 调用该方法,只设置第一个参数的值。

参考代码:

scala> def add(x:Int = 0, y:Int = 0) = x + y
add: (x: Int, y: Int)Int

scala> add(1)
res6: Int = 1

1.8.3 变长参数

如果方法的参数是不固定的,可以定义一个方法的参数是变长参数。语法格式如下:

def 方法名(参数名:参数类型*):返回值类型 = {
    方法体
}

在参数类型后面加一个 * 号,表示参数可以是0个或者多个。

示例:

  1. 定义一个计算若干个值相加的方法。
  2. 调用方法,传入以下数据:1,2,3,4,5。

参考代码:

scala> def add(num:Int*) = num.sum
add: (num: Int*)Int

scala> add(1,2,3,4,5)
res7: Int = 15

其他:Scala 允许指定最后一个参数是可变长度的,或者唯一一个参数。变长参数,在函数体内本质上是一个 Array 实例,也就是除了例子里面的 sum 外,我们也可以用 for 循环迭代它,如:

scala> def add(x:Int*): Int={
     | var result=0
     | for(i <- x) result += i
     | result
     | }
add: (x: Int*)Int

scala> add(1,2,3)
res8: Int = 6

虽然它本质上是一个 Array,但是你不可以将 Array 对象传递给它,如果要传递需要传递如 array:_* 表明将每个元素传递给它,而不是整个数组对象。如:

add(ar:_*)

1.9 方法调用方式

在 Scala 中,有以下几种方法调用方式,后缀调用法、中缀调用法、花括号调用法、无括号调用法,在后续编写 Spark、Flink 程序时,会使用到这些方法调用方式。

1.9.1 后缀调用法

这种方法与 Java 没有区别。

语法:

对象名.方法名(参数)

示例: 使用后缀法 Math.abs 求绝对值。

参考代码

scala> Math.abs(-1)
res10: Int = 1

scala> Math.abs(-3)
res11: Int = 3

1.9.2 中缀调用法

语法

对象名 方法名 参数
1 to 10 //如果有多个参数,使用括号括起来

示例: 使用中缀法 Math.abs 求绝对值。

scala> Math abs -3
res12: Int = 3

1.9.3 操作符即方法

来看一个表达式:

1 + 1

大家觉得上面的表达式像不像方法调用?在Scala 中,+ - * / % 等这些操作符和 Java 一样,但在 Scala 中,所有的操作符都是方法,操作符是一个方法名字是符号的方法。

1.9.4 花括号调用法

语法格式如下:

Math.abs{ 
    // 表达式1
    // 表达式2
}

方法只有一个参数,才能使用花括号调用法,因为块表达式只有一个返回值,所以只能给1个参数用。示例: 使用花括号调用法 Math.abs 求绝对值。参考代码如下:

scala> Math.abs{-10}
res13: Int = 10

1.9.5 无括号调用法

如果方法没有参数,可以省略方法名后面的括号。示例如下:

  1. 定义一个无参数的方法,打印 “hello amo”。
  2. 使用无括号调用法调用该方法。

参考代码:

scala> def m3()=println("hello amo")
m3: ()Unit

scala> m3()
hello amo

1.10 函数 – 重点掌握

Scala 支持函数式编程,编写 Spark/Flink 程序中,会大量使用到函数。函数和我们的对象一样, 在 Scala 中都是属于一等公民。

1.10.1 定义函数

简便语法:

val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体

函数是一个 对象(变量)。 类似于方法,函数也有输入参数和返回值,函数定义不需要使用 def 定义,无需指定返回值类型。

示例:

  1. 定义一个两个数值相加的函数。
  2. 调用该函数。

参考代码:

scala> val add = (x:Int, y:Int) => x + y
add: (Int, Int) => Int = 以上是关于3天极速掌握 Scala 语言:First Day的主要内容,如果未能解决你的问题,请参考以下文章

每天半小时掌握Scala(day 02)

Julia两天极速入门学习笔记

Julia两天极速入门学习笔记

Julia两天极速入门学习笔记

每天半小时掌握Scala(day 03)

每天半小时掌握Scala(day 04)