文章目录
Scala
https://www.scala-lang.org/
一、概述
Scala是一门多范式的编程语言,一种类似java的编程语言 [1] ,设计初衷是实现可伸缩的语言 [2] 、并集成面向对象编程和函数式编程的各种特性。
为什么要学习scala编程语言?
- 大数据开发工程师参与大数据领域中的多项开源项目都是scala编写,如:kafka、spark、flink等
- 下一代,可以替换JAVA编程语言
- 很多一线互联网公司,正在使用scala语言编写大数据处理应用
二、安装和使用
安装配置JDK(略)
C:\Users\Administrator>java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
安装配置Scala
下载地址:https://downloads.lightbend.com/scala/2.13.1/scala-2.13.1.msi
本次课程使用的版本为:scala-2.11.12.msi
注意:安装目录最好不要出现空格和中文字符
配置环境变量
C:\Users\Administrator>scala -version
Scala code runner version 2.11.12 -- Copyright 2002-2017, LAMP/EPFL
使用
-
命令行(REPL模式)
Scala解释器读到一个表达式,对它进行求值,将它打印出来,接着再继续读下一个表达式。这个过程被称做读取(READ)–求值(Evaluation)–打印(Print)–循环(Loop),即:REPL。
Welcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161). Type in expressions for evaluation. Or try :help. scala> 8*5 res0: Int = 40
8*5
执行计算后得到结果:40,结果被命名为res0
,类型为Int
-
手动编译运行模式
object HelloWorld{ def main(args:Array[String]):Unit = { println("Hello World") } }
- 创建scala的源文件==*.scala==
- 先编译
scalac *.scala
- 解释执行
scala *
-
IDE工具
以IntelliJ为例介绍,需要安装
scala plugin
三、变量和类型
常用类型
和Java一样,Scala中也有7种数值类型:Byte、Short、Int、Long、Double、Float、Char,以及1个Boolean类型。跟Java不同的是,这些类型都是类,归属于scala.*
包。还有一种常用类型为java.lang.String
用以声明字符串
表达式
表达式是可计算的语句。如:
1 + 1
你可以使用println
来输出表达式的结果。
// println 打印表达式的值 换行
// print 打印表达式的值 不换行
// 引用表达式的值打印
println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!
声明变量
除了可以重新赋值,变量和常量类似。你可以使用var
关键字来定义一个变量。
var x = 1 + 1
x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9
注意:scala类型可以被推断,或者你也可以显示地声明类型,语法
var x : Int = 10
声明常量(推荐)
你可以使用val
关键字来给表达式的结果命名。
val x = 1 + 1
println(x) // 2
x = 3 // This does not compile.
类型转换
值类型可以按照下面的方向进行转换:
例如:
val x: Long = 987654321
val y: Float = x // 9.8765434E8 (note that some precision is lost in this case)
val face: Char = '☺'
val number: Int = face // 9786
转换是单向,下面这样写将不会通过编译。
val x: Long = 987654321
val y: Float = x // 9.8765434E8
val z: Long = y // Does not conform
你可以将一个类型转换为子类型
运算符
-
算术运算符:
+、-、*、/、%
-
关系运算符:
==、!=、>、<、>=、<=
-
逻辑运算符:
&&、||、!
-
位运算符:
&(按位与)、|(按位或)、^(异或)、~(取反)、<<、>>、>>>(无符号)
2 00000010 3 00000011 2&3=2 00000010 & 同时为1 结果为1 不同为0 2|3=3 00000011 | 有一个为1 结果为1 不同为0 2^3=1 00000001 ^ 相同为0,不同为1 ~2=-3 11111101 2<<1=4 00000100 二进制 向左移动 n位 2>>1=1 >>>
-
赋值运算符:
=
注意:在Scala中,运算符即是方法。
10 + 1
可以写成10.+(1)
Scala类型层次结构【重点】
在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。
Any
是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如equals
、hashCode
和toString
。Any
有两个直接子类:AnyVal
和AnyRef
。AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double
、Float
、Long
、Int
、Short
、Byte
、Char
、Unit
和Boolean
。Unit
(类似于java中的void
)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()
。所有的函数必须有返回,所以说有时候Unit
也是有用的返回类型。AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef
的子类型。如果Scala被应用在Java的运行环境中,AnyRef
相当于java.lang.Object
。Nothing
是所有类型的子类型,也称为底部类型。没有一个值是Nothing
类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。Null
是所有引用类型的子类型(即AnyRef
的任意子类型)。它有一个单例值由关键字null
所定义。Null
主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null
的替代方案。
这里有一个例子,说明了字符串、整型、布尔值和函数都是对象,这一点和其他对象一样:
val list: List[Any] = List(
"a string",
732, // an integer
'c', // a character
true, // a boolean value
() => "an anonymous function returning a string"
)
list.foreach(element => println(element))
这里定义了一个类型List<Any>
的变量list
。这个列表里由多种类型进行初始化,但是它们都是scala.Any
的实例,所以可以把它们加入到列表中。
下面是程序的输出:
a string
732
c
true
<function>
四、条件分支
if(布尔表达式 1){
// 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){
// 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){
// 如果布尔表达式 3 为 true 则执行该语句块
}else {
// 如果以上条件都为 false 执行该语句块
}
if条件分支
如:
if ( n1 > 0) true
if…else条件分支
如:
val result = if ( n1 > 0) true else false
if…else if … else条件分支
val result = if (n1 > 0) 1
else if(n1 == 0) 0
else -1
注意:
- 代码块中只有一行内容,可以省略
{}
,如有多行代码需用{}
- 代码块的最后一个表达式的值就是块的值
- scala中没有switch语句,不过它有一个强大很多的模式匹配,后续介绍
五、循环
Scala的循环类似于Java的循环
while循环
while(boolean条件){
// 循环体
}
如:求1到100数值之和
// 控制台输入求和值
val input : String = readLine("请输入:")
var num = input.toInt
var sum = 0
while(num > 0){
sum = sum + num
num = num - 1
}
print(sum)
Note : scala中没有
++
和--
,可以使用+=1
或-=1
替换
do … while…循环
do{
//循环体
}while(boolean条件)
如:求1到100数值之和
do {
sum = sum.+(num)
num = num - 1
} while (num > 0)
print(sum)
for循环
Scala没有与Java for循环直接对应的结构,语法如下:
// m to n ,返回m到n(包含m)的Range
for (n <- 1 to 100) {
sum = sum + n
}
print(sum)
// m until n ,返回m到n(不包含m)的Range
for (n <- 1 until 100) {
sum = sum + n
}
print(sum)
嵌套for循环
如:九九乘法表
// 传统方式
for (m <- 1 to 9) {
for (n <- 1 to m) {
print(s"$m*$n=" + m * n + "\t")
if (m == n) println
}
}
// 推荐方式:多层循环用”;“分隔
for (m <- 1 to 9; n <- 1 to m) {
print(s"$m*$n=" + m * n + "\t")
if (m == n) println
}
for遍历集合
val list = List[String]("Hello World", "Hello Hadoop")
for (str <- list) {
println(str)
}
for循环过滤(if守卫)
if作为for守卫语句能实现过滤的效果,显著地降低循环次数,并且更易读,如下:
for (n <- 1 to 10 if n % 2 == 0 if n > 5) {
println(s"$n")
}
// 6 8 10
yield
for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合
- 针对每一次 for 循环的迭代, yield 会产生一个值,被循环记录下来 (内部实现上,像是一个缓冲区).
- 当循环结束后, 会返回所有 yield 的值组成的集合.
- 返回集合的类型与被遍历的集合类型是一致的.
val result = for (n <- 1 to 10 if n % 2 == 1 if n != 5) yield n * 2
println(result)
//Vector(2, 6, 14, 18)
// 如果您愿意:也可以将生成器、守卫和定义包含在花括号中,并以换行的方式分隔
val result = for {n <- 1 to 10
if n % 2 == 1
if n != 5}yield n * 2
println(result)
Break
Scala中没有提供break和continue关键词来退出循环,可以使用Breaks对象中的break方法终止循环
import scala.util.control.Breaks._
breakable {
for (n <- 1 to 10) {
if (n == 5) break
println(n)
}
}
六、函数
函数三要素:函数名、参数、函数体
声明函数语法
def functionName ([参数列表]) : [return type] = { 函数体}
示例
// 无返回值
def sayHi(str: String): Unit = {
println("Hello:" + str)
}
// 可省略返回值类型,支持自动推导
def sum(x: Int, y: Int) = x + y
def multi(x: Double, y: Double): Double = x * y
总结:
- 有返回值的函数,
: 返回值类型 = {函数代码块}
或者= {函数代码块}
- 无返回值的函数,
:Unit = {函数代码块}
或者{}
变长参数
package function
object Function02 {
// 主函数声明
def main(args: Array[String]) {
println(sum(1, 2, 3, 4, 5))
println(sum(1))
println(sum())
}
// 变长参数列表 n1 n2... nn
// 语法:变量名:类型*
def sum(nn: Int*): Int = {
var sum = 0
for (n <- nn) {
sum += n
}
sum
}
// 注意: 如果有多个参数,可变长参数需要写在所有参数之后
def sum2(default: Int,nn: Int*): Int = {
var sum = 0
for (n <- nn) {
sum += n
}
sum
}
}
参数默认值
package function
object Function03 {
def main(args: Array[String]): Unit = {
println(m1("Welcome"))
// 无给定值,使用默认值
println(m2())
// 给定值,使用给定值
println(m2("welcome", 20, 100.0))
// 部分参数使用给定值,另一部分参数使用默认值
println(m2("welcome"))
// 给特定的参数赋予给定值,其余使用默认值
println(m2(arg2 = 100))
}
// 默认值函数
def m1(arg1: String = "Hello"): String = {
arg1
}
// 默认值函数
// 注意:函数不支持重载,只有方法支持重载
def m2(arg1: String = "Hello", arg2: Int = 10, arg3: Double = 10.0): String = {
arg1 + "\t" + arg2 + "\t" + arg3
}
}
带名参数
m1(arg2 = 100, arg1 = "new value")
def m1(arg1: String = "default value1", arg2: Int = 10): Unit ={
println (arg1 + "\t" + arg2)
}
//new value <---> 100
Lazy值
懒加载,当val被声明为lazy时,它的初始化将被推迟,直到我们首次对它取值。
import scala.io.Source._
/**
* lazy值 使用时初始化 节省资源
*/
object function03 {
def main(args: Array[String]): Unit = {
val file1 = fromFile("").mkString
println(file1)
// lazy val file2 = fromFile("").mkString
// println(file2)
}
}
递归函数
递归求1到n之和
def sum(n: Int): Int = {
var total = 0
if (n >= 1) {
total = n + sum(n - 1)
}
total
}
递归求1到n的阶乘
def factorial(n: Int): Int = {
if (n > 1) {
n * factorial(n - 1)
} else {
1
}
}
匿名函数
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。
package function
/**
* 递归使用
*/
object Function05 {
def main(args: Array[String]): Unit = {
// 匿名函数: 没有函数名
val f1: Function2[Int, Int, Int] = (x: Int, y: Int) => x + y
println(f1)
val f2 = () => "Hello:"
println(f2)
val f3 = (x: Int, y: Int, z: Int) => x * y * z
println(f3)
List("a", "b").foreach(n => println(n))
/*
<function2>
<function0>
<function3>
a
b
*/
println(f1(1,2))
println(f2())
println(f2) // 打印对象,注意:没有参数的匿名函数需要加()调用
println(f3(2,2,2))
}
}
注意:
- 匿名函数返回的是一个函数对象(Function,scala提供了23个函数对象,分别对应0到22个参数)
柯里化(Currying)
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接收一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
package function
/**
* 柯里化函数(curring)
* 将多个参数的函数 转换为接收一个参数函数的过程,就称为柯里化,并返回以第二个参数为参数的函数
*/
object Function06 {
def main(args: Array[String]): Unit = {
println(sum2(1)(6))
println(sum3(1)(6))
println(sum4(1)(6)(2))
println(sum4 _)
}
// 普通函数
def sum1(x: Int, y: Int): Int = {
x + y
}
// 柯里化函数
def sum2(x: Int) = (y: Int) => x + y
// 柯里化函数 简化写法
def sum3(x: Int)(y: Int) = x + y
def sum4(x: Int)(y: Int)(z: Int) = x + y + z
}
七、数组操作
- 概念上类似于Java中的数组,一个特定类型的元素集合
数组的初始化
package array
object Array01 {
def main(args: Array[String]): Unit = {
// 数组声明
// a1数组的长度为10 泛型为Int
val a1 = new Array[Int](10)
val a2 = new Array[String](5)
// 指定数组 元素并初始化
val a3 = Array[Long](10,20,30)
val