首家大数据培训挂牌机构 股票代码:837906 | EN CN

Spark系列之二:Scala 语言解释及示例讲解

于2017-03-16由小牛君创建

分享到:


Scala 语言衍生自 Funnel 语言。Funnel 语言尝试将函数式编程和 Petri 网结合起来,而 Scala 的预期目标是将面向对象、函数式编程和强大的类型系统结合起来,同时让人要能写出优雅、简洁的代码。本文希望通过一系列 Java 与 Scala 语言编写的相同程序代码的对比,让读者能够尽快地熟悉 Scala 语言。

 

安装 Scala 并调试

首先,我们需要从官方网站下载最新的 Scala 运行包,官方网站的地址是 http://www.scala-lang.org/downloads,把下载的文件上传到 Linux 服务器并解压,然后进入解压后目录的 bin 目录,进入 Scala 编译器环境,如清单 1 所示。

清单 1. 进入 Scala 编译器
[root@localhost:4 bin]# ./scala
Welcome to Scala version 2.11.6 (OpenJDK 64-Bit Server VM, Java 1.7.0_65).
Type in expressions to have them evaluated.
Type :help for more information.

scala>

清单 1 显示我们使用的是 64 位操作系统,JDK1.7。

在正式讲解 Scala 之前,我们先来简单了解一下它。Scala 是一种解释性语言,可以直接翻译,如清单 2 所示,我们让 1 加上 3,编译器会直接输出整型 4。

清单 2. 整数相加
scala> 1+3
res0: Int = 4

清单 2 输出的 res0 表示变量名,Int 表示类型,输出值是 4,注意 Scala 是强类型语言,必须定义类型,但是 Scala 会帮助您判断数据类型。清单 2 所定义的变量 res0,我们可以直接操作它,如清单 3 所示。

清单 3. 变量乘法
scala> res0*3
res1: Int = 12

清单 3 里面解释器又自动输出一个变量 res1,注意 Scala 的所有变量都是对象,接下来我们在清单 4 所示程序里面把两个变量相乘。

清单 4. 变量相乘
scala> res0*res1
res2: Int = 48
清单 5. 输出文本
scala> println("hello world!")
hello world!

注意,这里由于 println 是 Scala 预定义导入的类,所以可以直接使用,其他非预定义的类,需要手动导入。

如果想要像执行 Shell 文件一样执行 Scala 程序,可以编写.scala 文件,如清单 6 所示。

清单 6. Scala 程序
[root@localhost:4 bin]# cat hello.scala
println("Hello, world, from a script!")
[root@localhost:4 bin]# ./scala hello.scala
Hello, world, from a script!

通过上面简单的介绍,读者应该可以上手写代码了,我们进入 Scala 简介章节。

 

Scala 简介

Scala 是一种把面向对象和函数式编程理念加入到静态类型语言中的语言,可以把 Scala 应用在很大范围的编程任务上,无论是小脚本或是大系统都是用 Scala 实现。Scala 运行在标准的 Java 平台上,可以与所有的 Java 库实现无缝交互。可以用来编写脚本把 Java 控件链接在一起。

函数式编程有两种理念做指导,第一种理念是函数是第一类值。在函数式语言中,函数也是值,例如整数和字符串,它们的地位相同。您可以把函数当作参数传递给其他函数,当作结果从函数中返回或保存在变量里。也可以在函数里定义其他函数,就好像在函数里定义整数一样。函数式编程的第二个主要理念是程序的操作符应该把输入值映射到输出值而不是就地修改数据。

Scala 程序会被编译为 JVM 的字节码。它们的执行期性能通常与 Java 程序一致。Scala 代码可以调用 Java 方法,访问 Java 字段,继承自 Java 类和实现 Java 接口。实际上,几乎所有 Scala 代码都极度依赖于 Java 库。

Scala 极度重用了 Java 类型,Scala 的 Int 类型代表了 Java 的原始整数类型 int,Float 代表了 float,Boolean 代表了 boolean,数组被映射到 Java 数组。Scala 同样重用了许多标准 Java 库类型。例如,Scala 里的字符串文本是 Java.lang.String,而抛出的异常必须是 java.lang.Throwable 的子类。

 

Scala 编程

Scala 的语法避免了一些束缚 Java 程序的固定写法。例如,Scala 里的分号是可选的,且通常不写。Scala 语法里还有很多其他地方省略了。例如,如何写类及构造函数,清单 7 所示分别采用 Java 和 Scala。

清单 7. 构造函数写法
//Java 代码
class MyClass { 
 private int index; 
 private String name; 
 public MyClass(int index, String name) {
 this.index = index; this.name = name; 
 } 
}
//Scala 代码
class MyClass(index: Int, name: String)

根据清单 7 所示代码,Scala 编译器将制造有两个私有字段的类,一个名为 index 的 int 类型和一个叫做 name 的 String 类型,还有一个用这些变量作为参数获得初始值的构造函数。

Scala 可以通过让您提升您设计和使用的接口的抽象级别来帮助您管理复杂性。例如,假设您有一个 String 变量 name,您想弄清楚是否 String 包含一个大写字符。清单 8 所示代码分别采用 Java 和 Scala。

清单 8. 验证是否大写字符
// Java 代码 
boolean nameHasUpperCase = false; 
for (int i = 0; i < name.length(); ++i) { 
if (Character.isUpperCase(name.charAt(i))) { 
 nameHasUpperCase = true; break; 
} 
} 
//Scala 代码
val nameHasUpperCase = name.exists(_.isUpperCase)

清单 8 所示代码,Java 代码把字符串看作循环中逐字符递进的底层级实体。

Scala 有两种类型的变量,val 和 var。val 变量的值只能初始化一次,再次赋值会发生错误,var 和 Java 的变量相同,可以随时修改。val 是函数式编程的风格,变量一旦赋值就不要再做修改。

清单 9 所示代码定义了变量并操作变量。

清单 9. Scala 变量操作
scala> val message = "hellp world"
message: String = hellp world
scala> val test = "1"
test: String = 1

scala> test ="2"
<console>:8: error: reassignment to val
 test ="2"
 ^
scala> var test1="1"
test1: String = 1

scala> test1="2"
test1: String = 2

清单 9 所示代码定义了变量 message、test,并对 test 重新赋值,由于 val 类型的变量是一次性的,所以抛出错误。var 类型的变量可以重新赋值,并输出新值。字符串支持多行定义,按回车后自动会换行,如清单 10 所示。

清单 10. Scala 定义多行字符
scala> val multiline=
 | "try multiple line"
multiline: String = try multiple line

scala> println(multiline)
try multiple line

在 Scala 里,定义方法采用 def 标示符,示例代码如清单 11 所示。

清单 11. 定义方法
scala> def max(x: Int, y: Int): Int = if(x < y) y else x 
max: (x: Int, y: Int)Int
scala> max(3,8)
res0: Int = 8

清单 11 所示代码定义了方法 Max,用于比较传入的两个参数的大小,输出较大值。

函数的定义用 def 开始。每个函数参数后面必须带前缀冒号的类型标注,因为 Scala 编译器没办法推断函数参数类型。清单 11 所定义的函数如图 1 所示,分别对函数体内的每一个元素列出了用途。

图 1. 函数定义解释

如清单 12 所示的 Java 和 Scala 代码,我们定义了一个函数 greet,调用该函数会打印出“Good Moring!”。

清单 12. 定义函数
//Java 代码
public static void main(String[] args){
 JavaScala.greet();
}
 
public static void greet(){
 System.out.println("Good Morning!");
}
//Scala 代码
scala> def greet()=println("Good Morning!")
greet: ()Unit
scala> greet();
Good Morning!

上例定义了 greet() 函数,编译器回应 greet 是函数名,空白的括号说明函数不带参数,Unit 是 greet 的结果类型。Unit 的结果类型指的是函数没有返回有用的值。Scala 的 Unit 类型接近于 Java 的 void 类型,而且实际上 Java 里每一个返回 void 的方法都被映射为 Scala 里返回 Unit 的方法。

注意,离开 Scala 编译器可以用:quit 或:q 命令。

与 Java 一样, 可以通过 Scala 的名为 args 的数组获得传递给 Scala 脚本的命令行参数。Scala 里,数组以零开始,通过在括号里指定索引访问一个元素。所以 Scala 里数组 steps 的第一个元素是 steps(0),而不是 Java 里的 steps[0]。清单 13 所示代码编写了一个 Scala 文件,定义读入第一个参数。

清单 13. 定义 Main 函数参数
[root@localhost:4 bin]# ./scala hello.scala zhoumingyao
Hello, world, from a script!zhoumingyao
[root@localhost:4 bin]# cat hello.scala
println("Hello, world, from a script!"+args(0))
//Java 代码
System.out.println("Hello, world, from a script!"+args(0));

当我们需要执行循环的时候,While 是一个不错的选择。清单 14 所示是 While 的实现。

清单 14. While 循环
[root@localhost:4 bin]# cat hello.scala
var i = 0;
while(i < args.length){
 println(args(i))
 i += 1
}
[root@localhost:4 bin]# ./scala hello.scala hello world ! this is zhoumingyao
hello
world
!
this
is
zhoumingyao

上面的 While 循环读取输入的参数,直到参数读取完毕。