3天极速掌握 Scala 语言:First Day
Posted Amo Xiang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3天极速掌握 Scala 语言:First Day相关的知识,希望对你有一定的参考价值。
目录
一、基础语法学习
1.1 声明变量
1.1.1 语法格式
在 Scala 中,可以使用 val 或者 var 关键字来定义变量,语法格式如下:
val/var 变量标识:变量类型 = 初始值
注意:
- val 定义的是 不可重新赋值的变量,不可变。 不是 java 中 final 的意思。Scala 也有 final,在 Scala 中 final 表示数值不可变以及不可被覆写。val 仅仅是数值不变,可以被复写。
- var 定义的是 可重新赋值的变量,可变。
- Scala 中定义变量类型写在变量名后面。
- Scala 的语句最后不需要添加分号。
- Scala 中类型是 首字母大写的。
1.1.2 在解释器中定义一个变量
示例:定义一个变量保存一个人的名字 “Amo”
步骤:
- 打开 Scala 解释器
- 定义一个字符串类型的变量用来保存名字
参考代码
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"${变量/表达式}字符串"
- 在定义字符串之前添加 s
- 在字符串中,可以使用 ${} 来引用变量或者编写表达式
示例: 请定义若干个变量,分别保存:“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 数据类型
基础类型 | 类型说明 |
---|---|
Byte | 8位带符号整数 |
Short | 16位带符号整数 |
Int | 32位带符号整数 |
Long | 64位带符号整数 |
Char | 16位无符号Unicode字符 |
String | Char类型的序列(字符串) |
Float | 32位单精度浮点数 |
Double | 64位双精度浮点数 |
Boolean | true或false |
Scala 类型与 Java 的区别:
- Scala 中所有的类型都使用 大写字母 开头。
- 整型使用 Int 而不是 Integer。
- Scala 中定义变量可以不写类型,让 Scala 编译器自动推断。
1.3.2 运算符
类别 | 操作符 |
---|---|
算术运算符 | +、-、*、/、%(加减乘除和取模) |
关系运算符 | >、<、==、!=、>=、<= |
逻辑运算符 | &&、||、! |
位运算符 | &、||、^、<<、>> |
Scala 中的运算符, 基本上和 Java 一样, 除了:
- Scala 中没有,++、-- 运算符
- 与 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 类型层次结构
所有的类型都是从 Any 继承了, 也就是我们 Scala 是一种单根继承体系。
类型 | 说明 |
---|---|
Any | 所有类型 的父类,它有两个子类 AnyRef 与 AnyVal |
AnyVal | 所有数值类型 的父类 |
AnyRef | 所有对象类型(引用类型)的父类 (String 本质就是 Char 数组,也是引用类型哦) |
Unit | 表示空,Unit 是 AnyVal 的子类,它只有一个的实例{% em %}() {% endem %} 它类似于 Java 中的 void,但 Scala 要比 Java 更加面向对象,Unit 本身也是一个类哦,当返回值为空的时候,返回的就是 Unit,Unit 的实例就是一个括号(). 这个对象用来做空返回值的时候用 |
Null | Null 也就是 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 不一样的是:
- 在 Scala 中,条件表达式也是有返回值的。
- 在 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 块表达式
- Scala 中,使用 {} 表示一个块表达式。
- 和 if 表达式一样,块表达式也是有值的。
- 值就是最后一个表达式的值(因为 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-10 的数字(提示:使用 to 方法)。
- 使用 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 类的 breakable 和 break 方法。
1.6.1 实现 break
用法
- 导入 Breaks 包
import scala.util.control.Breaks._
。 - 使用 breakable 将 for 表达式包起来。
- 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] = {
// 方法体:一系列的代码
}
- 参数列表的参数类型不能省略(因为之前定义变量的时候可以省略,这里不要混淆了,因为变量给了初始值,可以根据初始值推断类型)。
- 返回值类型可以省略,由 Scala 编译器自动推断。
- 返回值可以不写 return,默认就是 {}块表达式的值。
示例:
- 定义一个方法,实现两个整形数值相加,返回相加后的结果。
- 调用该方法。
参考代码
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 默认参数
在定义方法时可以给参数定义一个默认值。
示例:
- 定义一个计算两个值相加的方法,这两个值默认为 0。
- 调用该方法,不传任何参数。
参考代码
// 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 带名参数
在调用方法时,可以指定参数的名称来进行调用。
示例:
- 定义一个计算两个值相加的方法,这两个值默认为 0。
- 调用该方法,只设置第一个参数的值。
参考代码:
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,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 无括号调用法
如果方法没有参数,可以省略方法名后面的括号。示例如下:
- 定义一个无参数的方法,打印 “hello amo”。
- 使用无括号调用法调用该方法。
参考代码:
scala> def m3()=println("hello amo")
m3: ()Unit
scala> m3()
hello amo
1.10 函数 – 重点掌握
Scala 支持函数式编程,编写 Spark/Flink 程序中,会大量使用到函数。函数和我们的对象一样, 在 Scala 中都是属于一等公民。
1.10.1 定义函数
简便语法:
val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体
函数是一个 对象(变量)。 类似于方法,函数也有输入参数和返回值,函数定义不需要使用 def
定义,无需指定返回值类型。
示例:
- 定义一个两个数值相加的函数。
- 调用该函数。
参考代码:
scala> val add = (x:Int, y:Int) => x + y
add: (Int, Int) => Int = 以上是关于3天极速掌握 Scala 语言:First Day的主要内容,如果未能解决你的问题,请参考以下文章