Scala 的学习笔记
admin
2024-01-22 05:37:38
0

Scala 的学习笔记

文章目录

  • Scala 的学习笔记
    • 1. Scala 简介
      • 1.1 Scala 特性
        • 面向对象特性
        • 函数式编程
        • 静态类型
        • 扩展性
        • 并发性
      • 1.2 Scala Web 框架
    • 2. Scala 基础语法
      • 2.1 第一个Scala程序
        • 交互式编程
      • 2.2 脚本形式
      • 2.3 基本语法
      • 2.4 标识符
      • 2.5 Scala 关键字
      • 2.6 Scala 注释
      • 2.7 空行和空格
      • 2.8 换行符
      • 2.9 Scala 包
        • 定义包
        • 引用
    • 3. Scala 数据类型
      • 3.1 Scala基础字面量
        • 整形字面量
        • 浮点型字面量
        • 布尔型字面量
        • 符号字面量
        • 字符字面量
        • 字符串字面量
        • 多行字符串的表示方法
        • Null 值
        • Scala 转义字符
    • 4. Scala 变量
      • 4.1 变量声明
      • 4.2 变量类型声明
      • 4.3 变量类型引用
      • 4.4 Scala 多个变量声明
    • 5. Scala 访问修饰符
      • 5.1 私有(Private)成员
      • 5.2 保护(Protected)成员
      • 5.3 公共(Public)成员
      • 5.4 作用域保护
    • 6. Scala 运算符
      • 6.1 算数运算符
      • 6.2 关系运算符
      • 6.3 逻辑运算符
      • 6.4 位运算符
      • 6.5 赋值运算符
      • 6.6 运算符优先级
    • 7. Scala IF...ELSE 语句
        • if语句
        • if...else 语句
        • if...else if...else 语句
        • if...else 嵌套语句
    • 8. Scala 循环
      • 循环类型
      • 循环控制语句
      • 无限循环
      • Scala while 循环
      • Scala do...while 循环
      • Scala for循环
      • for 循环集合
      • for 循环过滤
      • for 使用 yield
    • 9. Scala 方法与函数
      • 方法声明
      • 方法定义
      • 方法调用
        • 函数传名调用(Call-by-Name)
        • Scala 指定函数参数名
        • Scala 函数 - 可变参数
        • Scala 递归函数
        • Scala 函数 - 默认参数值
        • Scala 高阶函数
        • Scala 函数嵌套
        • Scala 匿名函数
        • Scala 偏应用函数
        • Scala 函数柯里化(Currying)
    • 10.Scala 闭包
    • 11. Scala 字符串
      • 创建字符串
    • 12. Scala 数组
      • 声明数组
      • 处理数组
      • 多维数组
      • 合并数组
      • 创建区间数组
    • 13. Scala Collection(集合)
      • Scala List(列表)
        • 列表基本操作
        • 连接列表
        • List.fill()
        • List.tabulate()
        • List.reverse
      • Scala Set(集合)
        • 集合基本操作
        • 连接集合
        • 查找集合中最大与最小元素
        • 交集
      • Scala Map(映射)
        • Map 基本操作
        • Map 合并
        • 输出 Map 的 keys 和 values
      • Scala 元组
        • 迭代元组
        • 元组转为字符串
        • 元素交换
      • Scala Option(选项)
      • Scala Iterator(迭代器)
        • 查找最大与最小元素
    • 14. Scala 类和对象
      • Scala 继承
      • Scala 单例对象
        • 单例对象实例
        • 伴生对象实例
    • 15. Scala Trait(特征)
      • 特征构造顺序
    • 16. Scala 模式匹配
        • 使用样例类
    • 17. Scala 正则表达式
    • 18. Scala 异常处理
      • 抛出异常
      • 捕获异常
      • finally 语句
    • 19. Scala 提取器(Extractor)
      • 提取器使用模式匹配
    • 20、Scala 文件 I/O
      • 从屏幕上读取用户输入
      • 从文件上读取内容

1. Scala 简介

Scala 是 Scalable Language 的简写,是一门多范式的编程语言。

1.1 Scala 特性

面向对象特性

Scala 是一种纯面向对象的语言,每个值都是对象。对象的数据类型以及行为由类和特质描述。

类抽象机制的扩展有两种途径:一种途径是子类继承,另一种途径是灵活的混入机制。这两种途径能避免多重继承的种种问题。

函数式编程

Scala 也是一种函数式语言,其函数也能当成值来使用。Scala 提供了轻量级的语法用以定义匿名函数,支持高阶函数,允许嵌套多层函数,并支持柯里化。Scala 的case class 及其内置的模式匹配相当于函数式编程语言中常用的代数类型。更进一步,程序员可以利用Scala的模式匹配,编写类似正则表达式的代码处理XML数据。

静态类型

Scala具备类型系统,通过编译时检查,保证代码的安全性和一致性。类型系统具体支持以下特性:

  • 泛型类
  • 协变和逆变
  • 标注
  • 类型参数的上下限约束
  • 把类别和抽象类型作为对象成员
  • 复合类型
  • 引用自己时显示指定类型
  • 视图
  • 多态方法

扩展性

Scala的设计秉承一项事实,即在实践中,某个领域特定的应用程序开发往往需要特定于该领域的语言扩展。Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:

  • 任何方法可用作前缀或后缀操作符
  • 可以根据预期类型自动构造闭包

并发性

Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱发收消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。

1.2 Scala Web 框架

目前比较流程的 Scala 的Web应用框架:

  • Lift 框架
  • Play 框架

2. Scala 基础语法

Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的。

我们可以认为 Scala 程序是对象的集合,通过调用彼此的方法来实现消息传递。接下来来理解下:类、对象、方法、实例变量的概念:

  • 对象 - 对象有属性和行为。例如:一只狗的属性有:颜色,名字。行为有:叫、跑、吃等。对象是一个类的实例。
  • - 类是对象的抽象,而对象是类的具体实例。
  • 方法 - 方法描述的基本的行为,一个类可以包含多个方法。
  • 字段 - 每个对象都有它唯一的实例变量集合,即字段。对象的属性通过给字段赋值来创建。

2.1 第一个Scala程序

交互式编程

交互式编程不需要创建脚本文件,可以通过以下命令调用:

$ scala
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_31).
Type in expressions to have them evaluated.
Type :help for more information.scala> 1 + 1
res0: Int = 2scala> println("Hello World!")
Hello World!scala> 

2.2 脚本形式

也可以通过创建一个 HelloWorld.scala 的文件来执行代码,HelloWorld.scala 代码如下所示:

HelloWorld.scala 文件代码:

Object HelloWorld {/* 这是我的第一个 Scala 程序* 以下程序将输出'Hello World!'*/def main(args: Array[String]) = {println("Hello,world!") // 输出 Hello, world!}
}

接下来使用 scalac 命令编译它:

$ scalac HelloWorld.scala 
$ ls
HelloWorld$.class    HelloWorld.scala
HelloWorld.class 

编译后可以看到目录下生成了 HelloWorld.class 文件,该文件可以在 Java Virtual Machine(JVM) 上运行。编译后,我们可以使用以下命令来执行程序:

$ scala HelloWorld
Hello, world!

2.3 基本语法

Scala 基本语法需要注意以下几点:

  • 区分大小写 - Scala是大小写敏感的,这意味着标识 Hello 和 hello 在Scala中会有不同的含义。

  • 类名 - 对于所有的类名的第一个字母要大写。

    如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。

    示例:class MyFirstScalaClass

  • 方法名称 - 所有的方法名称的第一个字母用小写。

    如果若干单词被用户构成方法的名称,则第一个单词的第一个字母小写,除第一个单词外的每个单词的第一个字母应大写。

    示例:def myMethodName()

  • 程序文件名 - 程序文件的名称应该给与第项名称完全匹配(新版本不需要了,但建议保留这种习惯)。

    保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加“.scala”为文件扩展名。(如果文件名和对象名称不匹配,程序将无法编译。)

    示例:假设“Hello World” 是对象的名称。那么该文件应保存为 “HelloWorld.scala”

  • def main(args: Array[String]) - Scala 程序从main() 方法开始处理,这是每一个Scala程序的强制程序入口部分。

2.4 标识符

Scala 可以使用两种形式的标志符,字符数字和符号。

字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号“”在Scala中也看作为字母。然而以“” 在 Scala 中也看作为字母。然而以“”在Scala中也看作为字母。然而以“”开头的标识符为保留的 Scala 编译器产生的标识符使用,用用程序应该避免使用“$”开始的标识符,以免造成冲突。

Scala 的命名规则采用和 Java 类似的 camel 命名规则,首字符小写,比如 toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。符号标识符包含一个或多个符号,如+,:,?等,比如:

+ ++ ::: < ?> :->

Scala 内部实现时会使用转义的标识符,比如:-> 使用 coloncoloncolonminus$greater 来表示这个符号。因此如果你需要在 Java代码中访问:->方法,你需要使用 Scala 的内部名称 coloncoloncolonminus$greater。

2.5 Scala 关键字

下表中列出了 scala 保留关键字,我们不能使用以下关键字作为变量:

abstractcasecatchclass
defdoelseextends
falsefinalfinallyfor
forSomeifimplicitimport
lazymatchnewnull
objectovreridepackageprivate
protectedreturnsealedsuper
thisthrowtraittry
truetypevalvar
whilewithyield
-:==>
<-<:<%>:
#@

2.6 Scala 注释

Scala 类似 Java 支持单行和多行注释。多行注释可以嵌套,但必须正确嵌套,一个注释开始符号对应一个结束符号。注释在 Scala 编译中会被忽略,示例如下:

object HelloWorld {/* 这是一个 Scala 程序* 这是一行注释* 这里演示了多行注释*/def main(args: Array[String]) {// 输出 Hello World// 这是一个单行注释println("Hello, world!") }
}

2.7 空行和空格

一行中只有空格或者带有注释,Scala 会认为其是空行,会忽略它。标记可以被空格或者注释来分割。

2.8 换行符

Scala 是面向行的语言,语句可以用分号 (😉 结束或换行符。Scala 程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。例如

val s = "菜鸟教程"; println(s)

2.9 Scala 包

定义包

Scala 使用 package 关键字定义包,在 Scala 将代码定义到某个包中有两种方式:

第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。比如:

package com.runoob
class HelloWorld

第二种方法有些类似 C#,如:

package com.runoob {class HelloWorld 
}

第二种方法,可以在一个文件中定义多个包。

引用

Scala 使用 import 关键字引用包。

import java.awt.Color  // 引入Colorimport java.awt._  // 引入包内所有成员def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent...  // 因为引入了java.awt,所以可以省去前面的部分
}

import 语句可以出现在任何地方,而不是只能在文件顶部。import 的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。

如果想要引入包中的几个成员,可以使用 selector(选取器):

import java.awt.{Color, Font}// 重命名成员
import java.util.{HashMap => JavaHashMap}// 隐藏成员
import java.util.{HashMap => _, _} // 引入了util包的所有成员,但是HashMap被隐藏了

注意: 默认情况下,Scala 总会引入 java.lang._ 、 scala._ 和 Predef._,这里也能解释,为什么以scala开头的包,在使用时都是省去 scala.的。

3. Scala 数据类型

Scala 与 Java 有着相同的数据类型,下表列出了Scala支持的数据类型:

数据类型描述
Byte8位有符号补码整数。数值区间为 -128 到 127
Short16位有符号补码整数。数值区间为 -32768 到 32767
Int32位有符号补码整数。数值区间为 -2147483648 到 2147483647
Long64为有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807
Float32位,IEEE 754标准的单精度浮点数
Double64位 IEEE 754标准的双精度浮点数
Char16位无符号Unicode字符,区间值为 U+0000 到 U+FFFF
String字符序列
Booleantrue 或 false
Unit表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Nullnull 或空引用
NothingNothing 类型在 Scala 的类层级的最低端;它是任何其他类型的子类型。
AnyAny 是所有其他类的超类
AnyRefAnyRef类是Scala里所有引用类(reference class)的基类

上表中列出的数据类型都是对象,也就是说Scala没有Java中的原生类型。在Scala是可以对数字等基础类型调用方法的。

3.1 Scala基础字面量

整形字面量

整形字面量用于 Int 类型,如果表示 Long,可以在数字后面添加 L 或者小写 l 作为后缀。

0
035
21 
0xFFFFFFFF 
0777L

浮点型字面量

如果浮点数后面有 f 或者 F 后缀时,表示这是一个 Float 类型,否则就是一个 Double 类型的。示例如下:

0.0 
1e30f 
3.14159f 
1.0e100
.1

布尔型字面量

布尔型字面量有 true 和 false。

符号字面量

符号字面量被写成 '<标识符> ,这里 <标识符> 可以是任何字母或数字的标识(注意:不能以数字开头)。这种字面量被映射成预定义类 scala.Symbol 的实例。

如 :符号字面量 'x 是表达式 scala.Symbol(“x”) 的简写,符号字面量定义如下:

package scala
final case class Symbol private (name: String) {override def toString: String = "'" + name
}

字符字面量

在 Scala 字符变量使用单引号 ' 来定义,如下:

'a' 
'\u0041'
'\n'
'\t'

其中 \ 标识转义字符,其后可以跟 u0041 数字或者 \r\n 等固定的转义字符。

字符串字面量

在 Scala 字符串自勉联使用双引号 " 来定义,如下:

"Hello,\nWorld!"

多行字符串的表示方法

多行字符串用三个双引号来表示分隔符,格式为:"""..."""

示例如下:

val foo = """你好
hello world
世界"""

Null 值

空值是 scala.Null 类型

Scala.Null 和 scala.Nothing 是用统一的方式处理Scala面向对象类型系统的某些“边界情况”的特殊类型。NUll 类是null 引用对象类型,它是每个引用类(继承自 AnyRef 的类)的子类。Null 不兼容值类型。

Scala 转义字符

下表列出了常见的转义字符:

转义字符Unicode描述
\b\u0008退格(BS),将当前位置移到前一列
\t\u0009水平制表(HT)(跳到下一个TAB位置)
\n\u000a换行(LF),将当前位置移到下一行开头
\f\u000c换页(FF),将当前位置移到下页开头
\r\u000d回车(CR),将当前位置移到本行开头
\"\u0022代表一个双引号(") 字符
\'\u0027代表一个单引号(') 字符
\\\u005c代表一个反斜线字符 ‘\’

0 到 255 间的 Unicode 字符可以用一个八进制转义序列来表示,即反斜线"\" 后跟 最多三个八进制。

在字符或字符串中,反斜线和后面的字符序列不能构成一个合法的转义序列将会导致 编译错误。

如下示例演示了一些转义字符的使用:

object Test {def main(args: Array[String]) {println("Hello\tWorld\n\n" );}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
Hello    World

4. Scala 变量

变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。

基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,可以在这些变量中存储整数,小数或者字母。

4.1 变量声明

首先了解下变量和常量。

  • 变量:在程序运行过程中其值可能发生改变的量叫做变量。如:时间,年龄。
  • 常量:在程序运行过程中其值不会发生变化的量叫做常量。如:数值 3,字符’A’。

在 Scala 中,使用关键词 ”var“ 声明变量,使用关键词 ”val“ 声明常量。

声明变量示例如下:

var myVar : String = "Foo"
var myVar : String = "Too"

如上定义了变量 myVar,我们可以修改它。

声明常量示例如下:

val myVal : String = "Foo"

以上定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错。

4.2 变量类型声明

变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:

var VariableName : DataType [= Initial Value]
或
val VariableName : DataType [= Initial Value]

4.3 变量类型引用

在 Scala 中声明变量和常量不一定要指定数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。

所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。

var myVar = 10;
val myVal = "Hello, Scala!";

以上实例中,myVar 会被推断为 Int 类型,myVal 会被推断为 String 类型。

4.4 Scala 多个变量声明

Scala 支持多个变量的声明:

val xmax, ymax = 100 // xmax, ymax都声明为100

如果方法返回值是元组,我们可以使用 val 来声明一个元组:

scala> val pa = (40,"Foo")
pa: (Int, String) = (40,Foo)

5. Scala 访问修饰符

Scala 访问修饰符基本和 Java一样,分别有:private,protected,public。

如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。

Scala 中的private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。

5.1 私有(Private)成员

用 private 关键字修饰,带有此标记的成员尽在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。

实例:

class Outer{class Inner{private def f(){println("f")}class InnerMost{f() // 正确}}(new Inner).f() //错误
}

(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private ,而访问不在类 Inner 之内。

但在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。

Java 中允许这两种访问,因为它允许外部类访问内部类的私有成员。

5.2 保护(Protected)成员

在 Scala 中,对保护(Protected)成员的访问比 Java 更严格一些。因为它只允许保护成员在定义了该成员的类的子类中被访问。而在Java中,用protected 关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其它类也可以进行访问。

实例:

package p {class Super {protected def f() {println("f")}}class Sub extends Super {f()}class Other {(new Super).f() //错误}
}

如上,Sub 类对 f 的访问没有问题,因为 f 在 Super 中被声明为 protected,而 Sub 是 Super 的子类。相反,Other 对 f 的访问不被允许,因为 other 没有继承自 Super。而后者在 java 里同样被认可,因为 Other 与 Sub 在同一包里。

5.3 公共(Public)成员

Scala中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。

实例:

class Outer {class Inner {def f() { println("f") }class InnerMost {f() // 正确}}(new Inner).f() // 正确因为 f() 是 public
}

5.4 作用域保护

Scala中,访问修饰符可以通过使用限定词强调。格式为:

private[x]
或
protected[x]

这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作”这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对象可见外,对其他所有类都是private“。

这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。

实例:

package bobsrockets{package navigation{private[bobsrockets] class Navigator{protected[navigation] def useStarChart(){}class LegOfJourney{private[Navigator] val distance = 100}private[this] var speed = 200}}package launch{import navigation._object Vehicle{private[launch] val guide = new Navigator}}
}

上述例子中,类 Navigator 被标记为 private[bobsrockets] 就是说这个类对包含在 bobsrockets 包里的所有的类和对象可见。

比如说,从 Vehicle 对象里对 Navigator 的访问是被允许的,因为对象 Vehicle 包含在包 launch中,而 launch 包在 bobsrockets 中,相反,所有在包 bobsrockets 之外的代码都不能访问类 Navigator。

6. Scala 运算符

一个运算符是一个符号,用于告诉编译器来执行指定的数学运算和逻辑运算。

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

  • 算数运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符

6.1 算数运算符

下表列出了 Scala 支持的算数运算符。

假定变量 A为10,B 为 20:

运算符描述实例
+加号A + B 运算结果为 30
-减号A - B 运算结果为 -10
*乘号A * B 运算结果为 200
/除号B / A 运算结果为 2
%取余B % A 运算结果为 0

实例:

object Test {def main(args: Array[String]) {var a = 10;var b = 20;var c = 25;var d = 25;println("a + b = " + (a + b) );println("a - b = " + (a - b) );println("a * b = " + (a * b) );println("b / a = " + (b / a) );println("b % a = " + (b % a) );println("c % a = " + (c % a) );}
}

执行结果如下:

$ scalac Test.scala 
$ scala Test
a + b = 30
a - b = -10
a * b = 200
b / a = 2
b % a = 0
c % a = 5

6.2 关系运算符

下表列出了 Scala 支持的关系运算符。

假定变量 A为 10,B 为 20:

运算符描述实例
==等于(A == B) 运算结果为 false
!=不等于(A != B) 运算结果为 true
>大于(A > B) 运算结果为 false
<小于(A < B) 运算结果为 true
>=大于等于(A >= B) 运算结果为 false
<=小于等于(A <= B) 运算结果为 true

实例:

object Test {def main(args: Array[String]) {var a = 10;var b = 20;println("a == b = " + (a == b) );println("a != b = " + (a != b) );println("a > b = " + (a > b) );println("a < b = " + (a < b) );println("b >= a = " + (b >= a) );println("b <= a = " + (b <= a) );}
}

执行结果如下:

$ scalac Test.scala 
$ scala Test
a == b = false
a != b = true
a > b = false
a < b = true
b >= a = true
b <= a = false

6.3 逻辑运算符

下表列出了 Scala 支持的逻辑运算符。

假定变量 A 为 1,B 为 0:

运算符描述实例
&&逻辑与(A && B) 运算结果为 false
||逻辑或(A || B)运算结果为 true
!逻辑非!(A && B) 运算结果为 true

实例:

object Test {def main(args: Array[String]) {var a = true;var b = false;println("a && b = " + (a&&b) );println("a || b = " + (a||b) );println("!(a && b) = " + !(a && b) );}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a && b = false
a || b = true
!(a && b) = true

6.4 位运算符

位运算符用来对二进制位进行操作,~,&,|,^ 分别位取反,按位与,按位或,按位异或运算,如下表实例:

pqp&qp|qp^q
00000
01011
11110
10011

如果指定 A = 60;及 B = 13;两个变量对应的二进制为:

A = 0011 1100B = 0000 1101-------位运算----------A&B = 0000 1100A|B = 0011 1101A^B = 0011 0001~A  = 1100 0011

Scala 中的按位运算法则如下:

运算符描述实例
&按位与运算符(a & b) 输出结果 12 ,二进制解释: 0000 1100
|按位或运算符(a | b) 输出结果 61 ,二进制解释: 0011 1101
^按位异或运算符(a ^ b) 输出结果 49 ,二进制解释: 0011 0001
~按位取反运算符(~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。
<<左移动运算符a << 2 输出结果 240 ,二进制解释: 1111 0000
>>右移动运算符a >> 2 输出结果 15 ,二进制解释: 0000 1111
>>>无符号右移A >>>2 输出结果 15, 二进制解释: 0000 1111

实例:

object Test {def main(args: Array[String]) {var a = 60;           /* 60 = 0011 1100 */  var b = 13;           /* 13 = 0000 1101 */var c = 0;c = a & b;            /* 12 = 0000 1100 */println("a & b = " + c );c = a | b;            /* 61 = 0011 1101 */println("a | b = " + c );c = a ^ b;            /* 49 = 0011 0001 */println("a ^ b = " + c );c = ~a;               /* -61 = 1100 0011 */println("~a = " + c );c = a << 2;           /* 240 = 1111 0000 */println("a << 2 = " + c );c = a >> 2;           /* 15 = 1111 */println("a >> 2  = " + c );c = a >>> 2;          /* 15 = 0000 1111 */println("a >>> 2 = " + c );}
}

执行结果为:

$ scalac Test.scala 
$ scala Test
a & b = 12
a | b = 61
a ^ b = 49
~a = -61
a << 2 = 240
a >> 2  = 15
a >>> 2 = 15

6.5 赋值运算符

以下列出了 Scala 语言支持的赋值运算符:

运算符描述实例
=简单的赋值运算,指定右边操作数赋值给左边的操作数。C = A + B 将 A + B 的运算结果赋值给 C
+=相加后再赋值,将左右两边的操作数相加后再赋值给左边的操作数。C += A 相当于 C = C + A
-=相减后再赋值,将左右两边的操作数相减后再赋值给左边的操作数。C -= A 相当于 C = C - A
*=相乘后再赋值,将左右两边的操作数相乘后再赋值给左边的操作数。C *= A 相当于 C = C * A
/=相除后再赋值,将左右两边的操作数相除后再赋值给左边的操作数。C /= A 相当于 C = C / A
%=求余后再赋值,将左右两边的操作数求余后再赋值给左边的操作数。C %= A is equivalent to C = C % A
<<=按位左移后再赋值C <<= 2 相当于 C = C << 2
>>=按位右移后再赋值C >>= 2 相当于 C = C >> 2
&=按位与运算后赋值C &= 2 相当于 C = C & 2
^=按位异或运算符后再赋值C ^= 2 相当于 C = C ^ 2
|=按位或运算后再赋值CC |= 2 相当于 C = C | 2

实例:

object Test {def main(args: Array[String]) {var a = 10;      var b = 20;var c = 0;c = a + b;println("c = a + b  = " + c );c += a ;println("c += a  = " + c );c -= a ;println("c -= a = " + c );c *= a ;println("c *= a = " + c );a = 10;c = 15;c /= a ;println("c /= a  = " + c );a = 10;c = 15;c %= a ;println("c %= a  = " + c );c <<= 2 ;println("c <<= 2  = " + c );c >>= 2 ;println("c >>= 2  = " + c );c >>= a ;println("c >>= a  = " + c );c &= a ;println("c &= 2  = " + c );c ^= a ;println("c ^= a  = " + c );c |= a ;println("c |= a  = " + c );}
}

执行结果为:

$ scalac Test.scala 
$ scala Test
c = a + b  = 30
c += a  = 40
c -= a = 30
c *= a = 300
c /= a  = 1
c %= a  = 5
c <<= 2  = 20
c >>= 2  = 5
c >>= a  = 0
c &= 2  = 0
c ^= a  = 10
c |= a  = 10

6.6 运算符优先级

运算符优先级取决于所属的运算符组,它会影响算式的计算。

实例: x = 7 + 3 * 2; 这里, x 计算结果为 13, 而不是 20,因为乘法() 高于加法(+), 所以它先计算 32 再加上 7。

如下表格,邮件及从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最底的优先级。

类别运算符关联性
1() []左到右
2! ~右到左
3* / %左到右
4+ -左到右
5>> >>> <<左到右
6> >= < <=左到右
7== !=左到右
8&左到右
9^左到右
10|左到右
11&&左到右
12||左到右
13= += -= *= /= %= >>= <<= &= ^= |=右到左
14,左到右

7. Scala IF…ELSE 语句

Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True 或者False)来决定执行的代码块。

if语句

if 语句由布尔表达式及之后的语句块组成。

语法

if 语句的语法格式如下:

if(布尔表达式)
{// 如果布尔表达式为 true 则执行该语句块
}

如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。

实例:

object Test {def main(args: Array[String]) {var x = 10;if( x < 20 ){println("x < 20");}}
}

执行结果如下:

$ scalac Test.scala 
$ scala Test
x < 20

if…else 语句

if 语句后可以紧跟 else 语句,else 内的语句块可以再布尔表达式为false 的时候执行。

语法

if…else 的语法格式如下:

if(布尔表达式){// 如果布尔表达式为 true 则执行该语句块
}else{// 如果布尔表达式为 false 则执行该语句块
}

实例

object Test {def main(args: Array[String]) {var x = 30;if( x < 20 ){println("x 小于 20");}else{println("x 大于等于 20");}}
}

执行结果如下:

$ scalac Test.scala 
$ scala Test
x 大于等于 20

if…else if…else 语句

if 语句可以紧跟 else if…else 语句,在多个条件判断语句的情况下很有用。

语法

if…else if…else 语法格式如下:

if(布尔表达式 1){// 如果布尔表达式 1 为 true 则执行该语句块
}else if(布尔表达式 2){// 如果布尔表达式 2 为 true 则执行该语句块
}else if(布尔表达式 3){// 如果布尔表达式 3 为 true 则执行该语句块
}else {// 如果以上条件都为 false 执行该语句块
}

实例

object Test {def main(args: Array[String]) {var x = 30;if( x == 10 ){println("X 的值为 10");}else if( x == 20 ){println("X 的值为 20");}else if( x == 30 ){println("X 的值为 30");}else{println("无法判断 X 的值");}}
}

执行结果如下:

$ scalac Test.scala 
$ scala Test
X 的值为 30

if…else 嵌套语句

if…else 嵌套语句可以实现在 if 语句内嵌入一个或多个 if 语句。

语法

if…else 嵌套语句语法格式如下:

if(布尔表达式 1){// 如果布尔表达式 1 为 true 则执行该语句块if(布尔表达式 2){// 如果布尔表达式 2 为 true 则执行该语句块}
}

else if…else 的嵌套语句 类似 if…else 嵌套语句。

实例:

object Test {def main(args: Array[String]) {var x = 30;var y = 10;if( x == 30 ){if( y == 10 ){println("X = 30 , Y = 10");}}}
}

执行结果为:

$ scalac Test.scala 
$ scala Test
X = 30 , Y = 10

8. Scala 循环

有些时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的;函数中的第一个语句先执行,接着是第二个语句,依次类推。

编程语言提供了更为复杂执行路径的多种控制结构。

循环语句允许我们多次执行一个语句或语句组。

循环类型

Scala 语言提供了以下几种循环类型。

循环类型描述
while 循环运行一系列语句,如果条件为 true,会重复运行,直到条件变为 false。
do…while 循环类似 while 语句区别在于 判断循环条件之前,先执行一次循环的代码块。
for 循环用来重复执行一系列语句直到达成特定条件,一般通过在每次循环完成后增加计数器的值来实现。

循环控制语句

循环控制语句改变你代码的执行顺序,通过它你可以实现代码的跳转。

Scala 不支持 break 或 continue 语句,但从2.8 版本后提供了一种中断循环的方式:break 语句,但是和Java里面的break有点不同。

语法

Scala 中的 break 的语法有点不太一样,格式如下:

// 导入以下包
import scala.util.control._// 创建 Breaks 对象
val loop = new Breaks;// 在 breakable 中循环
loop.breakable{// 循环for(...){....// 循环中断loop.break;}
}

实例

import scala.util.control._object Test {def main(args: Array[String]) {var a = 0;val numList = List(1,2,3,4,5,6,7,8,9,10);val loop = new Breaks;loop.breakable {for( a <- numList){println( "Value of a: " + a );if( a == 4 ){loop.break;}}}println( "After the loop" );}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
Value of a: 1
Value of a: 2
Value of a: 3
Value of a: 4
After the loop

无限循环

如果条件永远为 true,则循环将变成无限循环。可以使用 while 语句来实现无限循环:

object Test {def main(args: Array[String]) {var a = 10;// 无限循环while( true ){println( "a 的值为 : " + a );}}
}

如上代码执行后循环会永久执行下去,可以使用 Ctrl + C键来中断无限循环。

Scala while 循环

只要给定的条件为true,Scala 语言中 的 while 循环语句会重复执行循环体内的代码块。

语法

Scala 语言中 while 循环的语法:

while(condition)
{statement(s);
}

在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。

condition 可以是任意的表达式,当为任意非零值时都为 true。当条件为 true 时执行循环。当条件为 false 时,退出循环,程序流将继续执行紧接着循环的下一条语句。

实例:

object Test {def main(args: Array[String]) {// 局部变量var a = 10;// while 循环执行while( a < 20 ){println( "Value of a: " + a );a = a + 1;}}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
value of a: 10
value of a: 11
value of a: 12
value of a: 13
value of a: 14
value of a: 15
value of a: 16
value of a: 17
value of a: 18
value of a: 19

Scala do…while 循环

不像 while 循环在循环头部测试循环条件,Scala 语言中,do…while 循环是在循环的尾部检查它的条件。

do…while 循环与 while 循环类似,但是 do…while 循环会确保至少执行一次循环。

语法

Scala 语言中 while 循环的语法:

do {statement(s);
} while( condition );

实例:

object Test {def main(args: Array[String]) {// 局部变量var a = 10;// do 循环do{println( "Value of a: " + a );a = a + 1;}while( a < 20 )}
}

执行结果为:

$ scalac Test.scala
$ scala Test
value of a: 10
value of a: 11
value of a: 12
value of a: 13
value of a: 14
value of a: 15
value of a: 16
value of a: 17
value of a: 18
value of a: 19

Scala for循环

for 循环允许编写一个执行指定次数的循环控制结构。

语法

Scala 语言中 for 循环的语法:

for( var x <- Range ){statement(s);
}

上述语法中,Range 可以是一个数字区间表示 i to j,或者 i until j。左箭头 <- 用于为变量x 赋值。

实例:

  1. 如下是一个使用了 i to j 语法(包含 j)的实例:
object Test {def main(args: Array[String]) {var a = 0;// for 循环for( a <- 1 to 10){println( "Value of a: " + a );}}
}

执行结果为:

$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 3
value of a: 4
value of a: 5
value of a: 6
value of a: 7
value of a: 8
value of a: 9
value of a: 10

2.如下是一个使用了 i until j 语法(不包含 j)的实例:

object Test {def main(args: Array[String]) {var a = 0;// for 循环for( a <- 1 until 10){println( "Value of a: " + a );}}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 3
value of a: 4
value of a: 5
value of a: 6
value of a: 7
value of a: 8
value of a: 9

在 for 循环中可以使用分号(😉 来设置多个区间,它将迭代给定区间所有的可能值。如下实例演示了两个区间的循环实例:

object Test {def main(args: Array[String]) {var a = 0;var b = 0;// for 循环for( a <- 1 to 3; b <- 1 to 3){println( "Value of a: " + a );println( "Value of b: " + b );}}
}

执行结果为:

$ scalac Test.scala
$ scala Test
Value of a: 1
Value of b: 1
Value of a: 1
Value of b: 2
Value of a: 1
Value of b: 3
Value of a: 2
Value of b: 1
Value of a: 2
Value of b: 2
Value of a: 2
Value of b: 3
Value of a: 3
Value of b: 1
Value of a: 3
Value of b: 2
Value of a: 3
Value of b: 3

for 循环集合

for 循环集合的语法如下:

for( x <- List ){statement(s);
}

上述语法中,LIst 变量是一个集合,for 循环会迭代所有集合的元素。

实例:

如下将循环数字集合,使用List() 来创建集合。

object Test {def main(args: Array[String]) {var a = 0;val numList = List(1,2,3,4,5,6);// for 循环for( a <- numList ){println( "Value of a: " + a );}}
}

执行结果为:

$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 3
value of a: 4
value of a: 5
value of a: 6

for 循环过滤

Scala 可以使用一个或多个 if 语句来过滤一些元素。

如下是在 for 循环中使用过滤器的语法。

for( var x <- Listif condition1; if condition2...){statement(s);
}

可以使用分号(;)来为表达式添加一个或多个的过滤条件。

实例:

object Test {def main(args: Array[String]) {var a = 0;val numList = List(1,2,3,4,5,6,7,8,9,10);// for 循环for( a <- numListif a != 3; if a < 8 ){println( "Value of a: " + a );}}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 4
value of a: 5
value of a: 6
value of a: 7

for 使用 yield

可以将 for 循环的返回值作为一个变量存储。语法格式如下:

var retVal = for{ var x <- Listif condition1; if condition2...
}yield x

实例:

object Test {def main(args: Array[String]) {var a = 0;val numList = List(1,2,3,4,5,6,7,8,9,10);// for 循环var retVal = for{ a <- numListif a != 3; if a < 8}yield a// 输出返回值for( a <- retVal){println( "Value of a: " + a );}}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 4
value of a: 5
value of a: 6
value of a: 7

9. Scala 方法与函数

Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。

Scala 中的方法跟 Java 的类似,方法是组成类的一部分。

Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。

Scala 中使用 val 语句可以定义函数,def 语句定义方法。

class Test{def m(x: Int) = x + 3val f = (x: Int) => x + 3
}

注:有些翻译上函数(function) 与方法(method) 是没有区别的。

方法声明

Scala 方法声明格式如下:

def functionName ([参数列表]) : [return type]

如果不写 等于号 和方法主题,那么方法会被隐式声明为 抽象(abstract),包含它的类型则也是一个抽象类型。

方法定义

方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号: 和方法的返回类型,一个等于号 =,最后是方法的主体。

Scala 方法定义格式如下:

def functionName ([参数列表]) : [return type] = {function bodyreturn [expr]
}

如上代码中 return type 可以是任意合法的 Scala 数据类型。参数列表中的参数可以使用逗号分割。

如下实例的功能是将两个传入的参数相加并求和:

object add{def addInt( a:Int, b:Int ) : Int = {var sum:Int = 0sum = a + breturn sum}
}

如果方法没有返回值,可以返回为 Unit,这个类似于 Java 的 void,实例如下:

object Hello{def printMe( ) : Unit = {println("Hello, Scala!")}
}

方法调用

Scala 提供了多种不同的方法调用方式:

以下是调用方法的标准格式:

functionName( 参数列表 )

如果方法使用了实例的对象来调用,我们可以使用类似 java 的格式(使用,号):

[instance.]functionName( 参数列表 )

以上实例演示了定义与调用方法的实例:

object Test {def main(args: Array[String]) {println( "Returned Value : " + addInt(5,7) );}def addInt( a:Int, b:Int ) : Int = {var sum:Int = 0sum = a + breturn sum}
}

执行结果为:

$ scalac Test.scala 
$ scala Test
Returned Value : 12

函数传名调用(Call-by-Name)

Scala的解释器在解析函数参数(function arguments)时有两种方式:

  • 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
  • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部

在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。

这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。

object Test {def main(args: Array[String]) {delayed(time());}def time() = {println("获取时间,单位为纳秒")System.nanoTime}def delayed( t: => Long ) = {println("在 delayed 方法内")println("参数: " + t)t}
}

上述实例中我们声明了 delayed 方法,该方法在变量名和变量类型使用 => 符号来设置传名调用。执行以上代码,输出结果如下:

$ scalac Test.scala 
$ scala Test
在 delayed 方法内
获取时间,单位为纳秒
参数: 241550840475831
获取时间,单位为纳秒

示例中 delay 方法打印了一条信息表示进入了该方法,接着 delay 方法打印接收到的值,最后再返回 t。

Scala 指定函数参数名

一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序像函数传递参数,实例如下:

object Test {def main(args: Array[String]) {printInt(b=5, a=7);}def printInt( a:Int, b:Int ) = {println("Value of a : " + a );println("Value of b : " + b );}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
Value of a :  7
Value of b :  5

Scala 函数 - 可变参数

Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。

Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:

object Test {def main(args: Array[String]) {printStrings("Runoob", "Scala", "Python");}def printStrings( args:String* ) = {var i : Int = 0;for( arg <- args ){println("Arg value[" + i + "] = " + arg );i = i + 1;}}
}

执行以上代码,输出结果为:

$ scalac Test.scala
$ scala Test
Arg value[0] = Runoob
Arg value[1] = Scala
Arg value[2] = Python

Scala 递归函数

递归函数在函数式编程的语言中起着重要的作用。

Scala 同样支持递归函数。

递归函数意味着函数可以调用它本身。

如下实例使用递归函数来计算阶乘:

object Test {def main(args: Array[String]) {for (i <- 1 to 10)println(i + " 的阶乘为: = " + factorial(i) )}def factorial(n: BigInt): BigInt = {  if (n <= 1)1  else    n * factorial(n - 1)}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
1 的阶乘为: = 1
2 的阶乘为: = 2
3 的阶乘为: = 6
4 的阶乘为: = 24
5 的阶乘为: = 120
6 的阶乘为: = 720
7 的阶乘为: = 5040
8 的阶乘为: = 40320
9 的阶乘为: = 362880
10 的阶乘为: = 3628800

Scala 函数 - 默认参数值

Scala 可以为函数参数指定默认参数值,使用了 默认参数,在调用函数的过程中可以不需要传递参数,这时函数就会调用它的默认参数值,如果传递了参数,则传递值会取代默认值。实例如下:

object Test {def main(args: Array[String]) {println( "返回值 : " + addInt() );}def addInt( a:Int=5, b:Int=7 ) : Int = {var sum:Int = 0sum = a + breturn sum}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
返回值 : 12

Scala 高阶函数

高阶函数(Higher-Order Function)就是操作其他函数的函数。

Scala 中允许使用高阶函数,高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。

如下实例中,apply() 函数使用了另外一个函数 f 和值 v 作为参数,而函数 f 又调用了参数 v:

object Test {def main(args: Array[String]) {println( apply( layout, 10) )}// 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 vdef apply(f: Int => String, v: Int) = f(v)def layout[A](x: A) = "[" + x.toString() + "]"}

执行结果如下:

$ scalac Test.scala
$ scala Test
[10]

Scala 函数嵌套

可以在 Scala 函数内定义函数,定义在函数内的函数称之为 局部函数。

以下示例实现阶乘运算,并使用内嵌函数:

object Test {def main(args: Array[String]) {println( factorial(0) )println( factorial(1) )println( factorial(2) )println( factorial(3) )}def factorial(i: Int): Int = {def fact(i: Int, accumulator: Int): Int = {if (i <= 1)accumulatorelsefact(i - 1, i * accumulator)}fact(i, 1)}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
1
1
2
6

Scala 匿名函数

Scala 中 定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。

使用匿名函数后,我们的代码变得更简洁了。

下面的表达式就定义了一个接受一个 Int 类型输入参数的匿名函数:

var inc = (x:Int) => x+1

上面定义的匿名函数,其实是下面这种写法的简写:

def add2 = new Function1[Int,Int]{  def apply(x:Int):Int = x+1;  
} 

以上实例的 inc 现在可作为一个函数,使用方式如下:

var x = inc(7)-1

同样我们可以在匿名函数中定义多个参数:

var mul = (x: Int, y: Int) => x*y

mul 现在可作为一个函数,使用方式如下:

println(mul(3, 4))

也可以不给匿名函数设置参数,如下所示:

var userDir = () => { System.getProperty("user.dir") }

userDir 现在可作为一个函数,使用方式如下:

println( userDir() )

实例:

object Demo {def main(args: Array[String]) {println( "multiplier(1) value = " +  multiplier(1) )println( "multiplier(2) value = " +  multiplier(2) )}var factor = 3val multiplier = (i:Int) => i * factor
}

将以上代码保持到 Demo.scala 文件中,执行以下命令:

$ scalac Demo.scala
$ scala Demo

输出结果为:

multiplier(1) value = 3
multiplier(2) value = 6

Scala 偏应用函数

Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。

如下实例,我们打印日志信息:

import java.util.Dateobject Test {def main(args: Array[String]) {val date = new Datelog(date, "message1" )Thread.sleep(1000)log(date, "message2" )Thread.sleep(1000)log(date, "message3" )}def log(date: Date, message: String)  = {println(date + "----" + message)}
}

执行结果为:

$ scalac Test.scala
$ scala Test
Mon Dec 02 12:52:41 CST 2018----message1
Mon Dec 02 12:52:41 CST 2018----message2
Mon Dec 02 12:52:41 CST 2018----message3

实例中,log() 方法接受两个参数:date 和 message。我们在程序执行时调用了三次,参数 date 值都相同,message 不同。

我们可以使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引赋给变量,以上实例修改如下:

import java.util.Dateobject Test {def main(args: Array[String]) {val date = new Dateval logWithDateBound = log(date, _ : String)logWithDateBound("message1" )Thread.sleep(1000)logWithDateBound("message2" )Thread.sleep(1000)logWithDateBound("message3" )}def log(date: Date, message: String)  = {println(date + "----" + message)}
}

执行结果为:

$ scalac Test.scala
$ scala Test
Tue Dec 18 11:25:54 CST 2018----message1
Tue Dec 18 11:25:54 CST 2018----message2
Tue Dec 18 11:25:54 CST 2018----message3

Scala 函数柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。

实例

首先我们定义一个函数:

def add(x:Int,y:Int)=x+y

那么在使用的时候,应该是这样用:add(1,2)

现在把这个函数变一下形:

def add(x:Int)(y:Int) = x + y

那么在使用的时候,应该是这样用:add(1)(2),最后结果一样都是3,这种方式(过程)就叫柯里化。

实现过程

add(1)(2) 实际上是一次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数y 调用这个函数类型的值。

实质上最先演变成这样一个方法:

def add(x:Int)=(y:Int)=>x+y

这个函数的意思是:接受一个x为参数,返回一个匿名函数,该匿名函数的定义是:接受一个 Int性参数y,函数体为 x+y。现在我来对这个方法进行调用。

val result = add(1) 

返回一个 result,那 result 的值应该是一个匿名函数:(y:int)=>1+y

所以为了得到结果,我们继续调用result。

val sum = result(2)

最后打印出来的结果就是3.

完整实例

下面是一个完整实例:

object Test {def main(args: Array[String]) {val str1:String = "Hello, "val str2:String = "Scala!"println( "str1 + str2 = " +  strcat(str1)(str2) )}def strcat(s1: String)(s2: String) = {s1 + s2}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
str1 + str2 = Hello, Scala!

10.Scala 闭包

闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。

闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。

如下面这段匿名的函数:

val multiplier = (i:Int) => i * 10  

函数体内有一个变量i,它作为函数的一个参数。如下面的另一段代码:

val multiplier = (i:Int) => i * factor

在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor 不是形式参数,而是自由变量,考虑下面代码:

var factor = 3  
val multiplier = (i:Int) => i * factor  

这里引入一个自由变量 factor,这个变量定义在函数外面。

这样定义的函数变量 multiplier 成为一个“闭包”,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。

完整实例

object Test {  def main(args: Array[String]) {  println( "muliplier(1) value = " +  multiplier(1) )  println( "muliplier(2) value = " +  multiplier(2) )  }  var factor = 3  val multiplier = (i:Int) => i * factor  
}

执行结果如下:

$ scalac Test.scala  
$  scala Test  
muliplier(1) value = 3  
muliplier(2) value = 6  

11. Scala 字符串

以下实例将字符串赋值给一个常量:

object Test {val greeting: String = "Hello,World!"def main(args: Array[String]) {println( greeting )}
}

以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)

在 Scala 中,字符串的类型实际上是 Java String,它本身没有String类。

在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着如果要修改字符串就会产生一个新的字符串对象。

但其它对象,如数组就是可变的对象。

创建字符串

创建字符串实例如下:

var greeting = "Hello World!";
或
var greeting:String = "Hello World!";

不一定要为字符串指定 String 类型,因为 Scala 编译器会自动推断出字符串的类型为 String。

当然也可以直接显示的声明字符串为 String 类型,如下实例:

object Test {val greeting: String = "Hello, World!"def main(args: Array[String]) {println( greeting )}
}

执行以上代码,输出结果为:

$ scalac Test.scala
$ scala Test
Hello, world!

Scala 中 String 对象是不可变的,如果需要创建一个可以修改的字符串,可以使用 String Builder 类,如下实例:

object Test {def main(args: Array[String]) {val buf = new StringBuilder;buf += 'a'buf ++= "bcdef"println( "buf is : " + buf.toString );}
}

执行以上代码,输出结果:

$ scalac Test.scala
$ scala Test
buf is : abcdef

12. Scala 数组

Scala 语言中提供的数组是用来存储固定大小的同类型元素,数组对于每一门编辑语言来说都是重要的数据结构之一。

声明数组变量并不是声明 number0、number1、…、number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用 number[0] 、numbers[1]、…numbers[99] 来表示一个个单独的变量。数组中某个指定的元素是通过索引来访问的。

数组的第一个元素索引为0,最后一个元素的索引为元素总数减1。

声明数组

以下是 Scala 数组声明的语法格式:

var z:Array[String] = new Array[String](3)
或
var z = new Array[String](3)

以上语法中,声明一个字符串类型的数组,数组长度为 3 ,可存储 3 个元素。我们可以为每个元素设置值,并通过索引来访问每个元素,如下所示:

z(0) = "Runoob"; z(1) = "Baidu"; z(4/2) = "Google"

最后一个元素的索引使用了表达式 4/2 作为索引,类似于 z(2) = “Google”。

也可以使用以下方式来定义一个数组:

var z = Array("Runoob", "Baidu", "Google")

处理数组

数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本的 for 循环。

以下实例演示了数组的创建,初始化等处理过程:

object Test {def main(args: Array[String]) {var myList = Array(1.9, 2.9, 3.4, 3.5)// 输出所有数组元素for ( x <- myList ) {println( x )}// 计算数组所有元素的总和var total = 0.0;for ( i <- 0 to (myList.length - 1)) {total += myList(i);}println("总和为 " + total);// 查找数组中的最大元素var max = myList(0);for ( i <- 1 to (myList.length - 1) ) {if (myList(i) > max) max = myList(i);}println("最大值为 " + max);}
}

执行结果为:

$ scalac Test.scala
$ scala Test
1.9
2.9
3.4
3.5
总和为 11.7
最大值为 3.5

多维数组

多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。如下定义了一个二维数组:

val myMatrix = Array.ofDim[Int](3, 3)

实例中数组中包含三个数组元素,每个数组元素又含有三个值。

import Array._object Test {def main(args: Array[String]) {val myMatrix = Array.ofDim[Int](3, 3)// 创建矩阵for (i <- 0 to 2) {for ( j <- 0 to 2) {myMatrix(i)(j) = j;}}// 打印二维阵列for (i <- 0 to 2) {for ( j <- 0 to 2) {print(" " + myMatrix(i)(j));}println();}}
}

执行结果如下:

$ scalac Test.scala
$ scala Test
0 1 2
0 1 2
0 1 2

合并数组

以下实例中,我们使用 concat() 方法来合并两个数组,concat() 方法中接受多个数组参数:

import Array._object Test {def main(args: Array[String]) {var myList1 = Array(1.9, 2.9, 3.4, 3.5)var myList2 = Array(8.9, 7.9, 0.4, 1.5)var myList3 =  concat( myList1, myList2)// 输出所有数组元素for ( x <- myList3 ) {println( x )}}
}

执行以上代码,输出结果为:

$ scalac Test.scala
$ scala Test
1.9
2.9
3.4
3.5
8.9
7.9
0.4
1.5

创建区间数组

以下实例中,使用了 range() 方法来生成一个区间范围内的数组,range() 方法最后一个参数为步长,默认为 1

import Array._object Test {def main(args: Array[String]) {var myList1 = range(10, 20, 2)var myList2 = range(10,20)// 输出所有数组元素for ( x <- myList1 ) {print( " " + x )}println()for ( x <- myList2 ) {print( " " + x )}}
}

执行结果为:

$ scalac Test.scala
$ scala Test
10 12 14 16 18
10 11 12 13 14 15 16 17 18 19

13. Scala Collection(集合)

Scala 提供了一套很好的集合实现,提供了一些集合类型的抽象。

Scala 集合分为可变的和不可变的集合。

可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。

而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。

如下使几种常用集合类型:

序号集合及描述
1Scala List(列表): List的特征是其元素以线性方式存储,集合中可以存放重复对象。
2Scala Set(列表): Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。
3Scala Map(映射): Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一堆见对象和值对象。
4Scala 元组: 元组是不同类型的值的集合
5Scala Option: Option[T] 表示有可能包含值的容器,也可能不包含值。
6Scala Iterator(迭代器):迭代器不是一个容器,更确切的说是逐一访问容器内元素的方法。

实例:

以下代码判断,演示了所有以上集合类型的定义实例:

// 定义整型 List
val x = List(1,2,3,4)// 定义 Set
val x = Set(1,3,5,7)// 定义 Map
val x = Map("one" -> 1, "two" -> 2, "three" -> 3)// 创建两个不同类型元素的元组
val x = (10, "Runoob")// 定义 Option
val x:Option[Int] = Some(5)

Scala List(列表)

Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦而被定义了就不能改变,其次列表具有递归的结构(也就是链接表结构)而数组不是。

列表的元素类型 T 可以写成 List[T]。例如,以下列出了多种类型的列表:

实例:

// 字符串列表
val site: List[String] = List("Runoob", "Google", "Baidu")// 整型列表
val nums: List[Int] = List(1, 2, 3, 4)// 空列表
val empty: List[Nothing] = List()// 二维列表
val dim: List[List[Int]] =List(List(1, 0, 0),List(0, 1, 0),List(0, 0, 1))

构造列表的两个基本单位是 Nil 和 ::

Nil 也可以表示为一个空列表。

以上实例我们可以写成如下所示:

// 字符串列表
val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))// 整型列表
val nums = 1 :: (2 :: (3 :: (4 :: Nil)))// 空列表
val empty = Nil// 二维列表
val dim = (1 :: (0 :: (0 :: Nil))) ::(0 :: (1 :: (0 :: Nil))) ::(0 :: (0 :: (1 :: Nil))) :: Nil

列表基本操作

Scala 列表有三个基本操作:

  • head 返回列表第一个元素
  • tail 返回一个列表,包含除了第一元素之外的其他元素
  • isEmpty 在列表为空时返回 true

对于 Scala 列表的任何操作都可以使用这三个基本操作来表达。实例如下:

// 字符串列表
object Test {def main(args: Array[String]) {val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))val nums = Nilprintln( "第一网站是 : " + site.head )println( "最后一个网站是 : " + site.tail )println( "查看列表 site 是否为空 : " + site.isEmpty )println( "查看 nums 是否为空 : " + nums.isEmpty )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
第一网站是 : Runoob
最后一个网站是 : List(Google, Baidu)
查看列表 site 是否为空 : false
查看 nums 是否为空 : true

连接列表

可以使用 ::: 运算符或 List.::😦) 方法或 List.concat() 方法来连接两个或多个列表。实例如下:

object Test {def main(args: Array[String]) {val site1 = "Runoob" :: ("Google" :: ("Baidu" :: Nil))val site2 = "Facebook" :: ("Taobao" :: Nil)// 使用 ::: 运算符var fruit = site1 ::: site2println( "site1 ::: site2 : " + fruit )// 使用 List.:::() 方法fruit = site1.:::(site2)println( "site1.:::(site2) : " + fruit )// 使用 concat 方法fruit = List.concat(site1, site2)println( "List.concat(site1, site2) : " + fruit  )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
site1 ::: site2 : List(Runoob, Google, Baidu, Facebook, Taobao)
site1.:::(site2) : List(Facebook, Taobao, Runoob, Google, Baidu)
List.concat(site1, site2) : List(Runoob, Google, Baidu, Facebook, Taobao)

List.fill()

可以使用 List.fill() 方法来创建一个指定重复数量的元素列表:

object Test {def main(args: Array[String]) {val site = List.fill(3)("Runoob") // 重复 Runoob 3次println( "site : " + site  )val num = List.fill(10)(2)         // 重复元素 2, 10 次println( "num : " + num  )}
}

执行结果如下:

$ vim Test.scala 
$ scala Test.scala 
site : List(Runoob, Runoob, Runoob)
num : List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)

List.tabulate()

List.tabulate() 方法是通过给定的函数来创建列表。

方法的第一个参数为元素的数量,可以是二维的,第二个参数为指定的函数,我们通过指定的函数计算结果并返回值插入到列表中,起始值为0,实例如下:

object Test {def main(args: Array[String]) {// 通过给定的函数创建 5 个元素val squares = List.tabulate(6)(n => n * n)println( "一维 : " + squares  )// 创建二维列表val mul = List.tabulate( 4,5 )( _ * _ )      println( "多维 : " + mul  )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
一维 : List(0, 1, 4, 9, 16, 25)
多维 : List(List(0, 0, 0, 0, 0), List(0, 1, 2, 3, 4), List(0, 2, 4, 6, 8), List(0, 3, 6, 9, 12))

List.reverse

List.reverse 用于将列表的顺序反转,实例如下:

object Test {def main(args: Array[String]) {val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))println( "site 反转前 : " + site )println( "site 反转后 : " + site.reverse )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
site 反转前 : List(Runoob, Google, Baidu)
site 反转后 : List(Baidu, Google, Runoob)

Scala Set(集合)

Scala Set(集合) 是没有重复的对象集合,所有的元素都是唯一的。

Scala 集合分为可变的和不可变的集合。

默认情况下,Scala 使用的是不可变结合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。

默认引用 scala.collection.immutable.Set,不可变集合实例如下:

val set = Set(1,2,3)
println(set.getClass.getName) //println(set.exists(_ % 2 == 0)) //true
println(set.drop(1)) //Set(2,3)

如果需要使用可变集合需要引入 scala.collection.mutable.Set:

import scala.collection.mutable.Set // 可以在任何地方引入 可变集合val mutableSet = Set(1,2,3)
println(mutableSet.getClass.getName) // scala.collection.mutable.HashSetmutableSet.add(4)
mutableSet.remove(1)
mutableSet += 5
mutableSet -= 2println(mutableSet) // Set(5, 3, 4)val another = mutableSet.toSet
println(another.getClass.getName) // scala.collection.immutable.Set

注意: 虽然可变Set和不可变Set都有添加或删除元素的操作,但是有一个非常大的差别。对不可变Set进行操作,会产生一个新的set,原来的set 并没有改变,这与List一样。而对可变Set进行操作,改变的是该Set本身,与ListBuffer类似。

集合基本操作

Scala 集合有三个基本操作:

  • head 返回集合第一个元素
  • tail 返回一个集合,包含除了第一元素之外的其他元素
  • isEmpty 在集合为空时返回true

对于 Scala 集合的任何操作都可以使用着三个基本操作来表达。示例如下:

object Test {def main(args: Array[String]) {val site = Set("Runoob", "Google", "Baidu")val nums: Set[Int] = Set()println( "第一网站是 : " + site.head )println( "最后一个网站是 : " + site.tail )println( "查看列表 site 是否为空 : " + site.isEmpty )println( "查看 nums 是否为空 : " + nums.isEmpty )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
第一网站是 : Runoob
最后一个网站是 : Set(Google, Baidu)
查看列表 site 是否为空 : false
查看 nums 是否为空 : true

连接集合

可以使用 ++ 运算符或 Set.++() 方法来连接两个集合。如果元素有重复的就会移除重复的元素。示例如下:

object Test {def main(args: Array[String]) {val site1 = Set("Runoob", "Google", "Baidu")val site2 = Set("Faceboook", "Taobao")// ++ 作为运算符使用var site = site1 ++ site2println( "site1 ++ site2 : " + site )//  ++ 作为方法使用site = site1.++(site2)println( "site1.++(site2) : " + site )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
site1 ++ site2 : Set(Faceboook, Taobao, Google, Baidu, Runoob)
site1.++(site2) : Set(Faceboook, Taobao, Google, Baidu, Runoob)

查找集合中最大与最小元素

可以使用 Set.min 方法来查找集合中的最小元素,使用 Set.max 方法查找集合中的最大元素。实例如下:

object Test {def main(args: Array[String]) {val num = Set(5,6,9,20,30,45)// 查找集合中最大与最小元素println( "Set(5,6,9,20,30,45) 集合中的最小元素是 : " + num.min )println( "Set(5,6,9,20,30,45) 集合中的最大元素是 : " + num.max )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
Set(5,6,9,20,30,45) 集合中的最小元素是 : 5
Set(5,6,9,20,30,45) 集合中的最大元素是 : 45

交集

可以使用 Set.& 方法或 Set.intersect 方法来查看两个集合的交集元素。实例如下:

object Test {def main(args: Array[String]) {val num1 = Set(5,6,9,20,30,45)val num2 = Set(50,60,9,20,35,55)// 交集println( "num1.&(num2) : " + num1.&(num2) )println( "num1.intersect(num2) : " + num1.intersect(num2) )}
}

执行结果为:

$ vim Test.scala 
$ scala Test.scala 
num1.&(num2) : Set(20, 9)
num1.intersect(num2) : Set(20, 9)

Scala Map(映射)

Map(映射)是一种可迭代的键值对(key/value)结构。

所有的值都可以通过对应的键来获取。

Map 中的键都是唯一的。

Map 也叫哈希表(Hash tables)。

Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以。

默认情况下 Scala 使用不可变 Map。如果你需要使用可变集合,需要显示的引入 import scala.collection.mutable.Map

在 Scala 中,你可以同时使用可变与不可变 Map,不可变的直接使用 Map,可变的使用 mutable.Map。以下实例演示了不可变 Map 的应用:

// 空哈希表,键为字符串,值为整型
var A:Map[Char,Int] = Map()// Map 键值对演示
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")

定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号,如下所示:

A += ('I' -> 1)
A += ('J' -> 5)
A += ('K' -> 10)
A += ('L' -> 100)

Map 基本操作

Scala Map 有三个基本操作:

方法描述
keys返回 Map 所有的键(key)
values返回 Map 所有的值(value)
isEmpty在 Map 为空时返回 true

实例

object Test {def main(args: Array[String]) {val colors = Map("red" -> "#FF0000","azure" -> "#F0FFFF","peru" -> "#CD853F")val nums: Map[Int, Int] = Map()println( "colors 中的键为 : " + colors.keys )println( "colors 中的值为 : " + colors.values )println( "检测 colors 是否为空 : " + colors.isEmpty )println( "检测 nums 是否为空 : " + nums.isEmpty )}
}

执行结果如下:

$ scalac Test.scala 
$ scala Test
colors 中的键为 : Set(red, azure, peru)
colors 中的值为 : MapLike(#FF0000, #F0FFFF, #CD853F)
检测 colors 是否为空 : false
检测 nums 是否为空 : true

Map 合并

可以使用 ++ 运算符或 Map.++() 方法来连接两个 Map,Map 合并时会移除重复的 key。以下演示了两个 Map 合并的实例:

object Test {def main(args: Array[String]) {val colors1 = Map("red" -> "#FF0000","azure" -> "#F0FFFF","peru" -> "#CD853F")val colors2 = Map("blue" -> "#0033FF","yellow" -> "#FFFF00","red" -> "#FF0000")//  ++ 作为运算符var colors = colors1 ++ colors2println( "colors1 ++ colors2 : " + colors )//  ++ 作为方法colors = colors1.++(colors2)println( "colors1.++(colors2) : " + colors )}
}

执行结果为:

$ scalac Test.scala 
$ scala Test
colors1 ++ colors2 : Map(blue -> #0033FF, azure -> #F0FFFF, peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)
colors1.++(colors2) : Map(blue -> #0033FF, azure -> #F0FFFF, peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)

输出 Map 的 keys 和 values

以下通过 foreach 循环输出 Map 中的 keys 和 values:

object Test {def main(args: Array[String]) {val sites = Map("runoob" -> "http://www.runoob.com","baidu" -> "http://www.baidu.com","taobao" -> "http://www.taobao.com")sites.keys.foreach{ i =>  print( "Key = " + i )println(" Value = " + sites(i) )}}
}

执行结果为:

$ scalac Test.scala 
$ scala Test
Key = runoob Value = http://www.runoob.com
Key = baidu Value = http://www.baidu.com
Key = taobao Value = http://www.taobao.com

Scala 元组

与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。

元组的值是通过将单个的值包含在圆括号中构成的。例如:

val t = (1, 3.14, "Fred")  

以上实例在元组中定义了三个元素,对应的类型分别为 [Int, Double, java.lang.String]。

此外我们也可以使用以下方式来定义:

val t = new Tuple3(1, 3.14, "Fred")

元组的实际类型取决于它的元素的类型,比如 (99, “runoob”) 是 Tuple2[Int, String]。 (‘u’, ‘r’, “the”, 1, 4, “me”) 为 Tuple6[Char, Char, String, Int, Int, String]。

目前 Scala 支持的元组最大长度为 22.对于 更大成都你可以使用集合,或者扩展元组。

访问元组的元素可以通过数字索引,如下一个元组:

val t = (4,3,2,1)

我们可以使用 t._1 访问第一个元素,t._2访问第二个元素,如下所示:

object Test {def main(args: Array[String]) {val t = (4,3,2,1)val sum = t._1 + t._2 + t._3 + t._4println( "元素之和为: "  + sum )}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
元素之和为: 10

迭代元组

可以使用 Tuple.productIterator() 方法来迭代输出元组的所有元素:

object Test {def main(args: Array[String]) {val t = (4,3,2,1)t.productIterator.foreach{ i =>println("Value = " + i )}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Value = 4
Value = 3
Value = 2
Value = 1

元组转为字符串

可以使用 Tuple.toString() 方法将元组的所有元素组合成一个字符串,实例如下:

object Test {def main(args: Array[String]) {val t = new Tuple3(1, "hello", Console)println("连接后的字符串为: " + t.toString() )}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
连接后的字符串为: (1,hello,scala.Console$@4dd8dc3)

元素交换

可以使用 Tuple.swap 方法来交换元组的元素。如下实例:

object Test {def main(args: Array[String]) {val t = new Tuple2("www.google.com", "www.runoob.com")println("交换后的元组: " + t.swap )}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
交换后的元组: (www.runoob.com,www.google.com)

Scala Option(选项)

Scala Option (选项) 类型用来表示一个值是可选的 (有值或无值)。

Option[T] 是一个类型为 T 的可选值的容器:如果值存在,Option[T] 就是一个 Some[T],如果不存在,Option[T] 就是对象 None。

如下:

// 虽然 Scala 可以不定义变量的类型,不过为了清楚些,我还是
// 把他显示的定义上了val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")println(value1) // Some("value1")
println(value2) // None

在上面的代码中,myMap 一个是一个 Key 的类型是 String,Value 的类型是 String 的 hash map,但不一样的是他的 get() 返回的是一个叫 Option[String] 的类别。

Scala 使用 option[String] 来告诉你: 我会想办法回传一个 String,但也可能没有 String 跟你。

myMap 里并没有 key2 这笔数据,get() 方法返回 None。

Option 有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 String,而你可以透过过 get() 这个函式拿到那个 String,如果他 返回地式 None,则代表没有字符串可以给你。

另一个实例:

object Test {def main(args: Array[String]) {val sites = Map("runoob" -> "www.runoob.com", "google" -> "www.google.com")println("sites.get( \"runoob\" ) : " +  sites.get( "runoob" )) // Some(www.runoob.com)println("sites.get( \"baidu\" ) : " +  sites.get( "baidu" ))  //  None}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
sites.get( "runoob" ) : Some(www.runoob.com)
sites.get( "baidu" ) : None

也可以通过模式匹配来输出匹配值。实例如下:

object Test {def main(args: Array[String]) {val sites = Map("runoob" -> "www.runoob.com", "google" -> "www.google.com")println("show(sites.get( \"runoob\")) : " +  show(sites.get( "runoob")) )println("show(sites.get( \"baidu\")) : " +  show(sites.get( "baidu")) )}def show(x: Option[String]) = x match {case Some(s) => scase None => "?"}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
show(sites.get( "runoob")) : www.runoob.com
show(sites.get( "baidu")) : ?

Scala Iterator(迭代器)

Scala Iterator (迭代器) 不是一个集合,它是一种用于访问集合地方法。

迭代器 it 的两个基本操作是 next 和 hasNext。

调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

调用 it.hasNext() 用于检测集合中是否还有元素。

让迭代器 it 逐个返回所有元素嘴贱但的方法是使用 while 循环:

object Test {def main(args: Array[String]) {val it = Iterator("Baidu", "Google", "Runoob", "Taobao")while (it.hasNext){println(it.next())}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Baidu
Google
Runoob
Taobao

查找最大与最小元素

可以使用 it.minit.max 方法从迭代器中查找最大与最小元素,实例如下:

object Test {def main(args: Array[String]) {val ita = Iterator(20,40,2,50,69, 90)val itb = Iterator(20,40,2,50,69, 90)println("最大元素是:" + ita.max )println("最小元素是:" + itb.min )}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
最大元素是:90
最小元素是:2

获取迭代器地长度

可以使用 it.sizeit.length 方法来查看迭代器中的元素个数。实例如下:

object Test {def main(args: Array[String]) {val ita = Iterator(20,40,2,50,69, 90)val itb = Iterator(20,40,2,50,69, 90)println("ita.size 的值: " + ita.size )println("itb.length 的值: " + itb.length )}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
ita.size 的值: 6
itb.length 的值: 6

14. Scala 类和对象

类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。

可以使用 new 关键字来创建类的对象,实例如下:

class Point(xc: Int, yc: Int) {var x: Int = xcvar y: Int = ycdef move(dx: Int, dy: Int) {x = x + dxy = y + dyprintln ("x 的坐标点: " + x);println ("y 的坐标点: " + y);}
}

Scala 中的类不声明为 public,一个 Scala 源文件中可以有多个类。

以上实例的类定义了两个变量 xy,一个方法:move,方法没有返回值。

Scala 的类定义可以有参数,称为类参数,如上面的 xc,yc,类参数在整个类中都可以访问。

然后可以使用 new 来实例化类,并访问类中的方法和变量:

import java.io._class Point(xc: Int, yc: Int) {var x: Int = xcvar y: Int = ycdef move(dx: Int, dy: Int) {x = x + dxy = y + dyprintln ("x 的坐标点: " + x);println ("y 的坐标点: " + y);}
}object Test {def main(args: Array[String]) {val pt = new Point(10, 20);// 移到一个新的位置pt.move(10, 10);}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 的坐标点: 20
y 的坐标点: 30

Scala 继承

Scala 继承一个基类跟 Java 很相似,但我们需要注意以下几点:

  • 1、重写一个非抽象方法必须使用 override 修饰符。
  • 2、只有主构造函数才可以往基类的构造函数里写参数。
  • 3、在子类中重写超累的抽象方法时,你不需要使用 override关键字

例子:

class Point(xc: Int, yc: Int) {var x: Int = xcvar y: Int = ycdef move(dx: Int, dy: Int) {x = x + dxy = y + dyprintln ("x 的坐标点: " + x);println ("y 的坐标点: " + y);}
}class Location(override val xc: Int, override val yc: Int,val zc :Int) extends Point(xc, yc){var z: Int = zcdef move(dx: Int, dy: Int, dz: Int) {x = x + dxy = y + dyz = z + dzprintln ("x 的坐标点 : " + x);println ("y 的坐标点 : " + y);println ("z 的坐标点 : " + z);}
}

Scala 使用 extends 关键字来继承一个类。实例中 Location 类继承了 Point 类。Point 称为父类(基类),Location 称为子类。

override val xc 为重写了父类的字段。

继承会继承父类的所有属性和方法,Scala只允许继承一个父类。

实例如下:

import java.io._class Point(val xc: Int, val yc: Int) {var x: Int = xcvar y: Int = ycdef move(dx: Int, dy: Int) {x = x + dxy = y + dyprintln ("x 的坐标点 : " + x);println ("y 的坐标点 : " + y);}
}class Location(override val xc: Int, override val yc: Int,val zc :Int) extends Point(xc, yc){var z: Int = zcdef move(dx: Int, dy: Int, dz: Int) {x = x + dxy = y + dyz = z + dzprintln ("x 的坐标点 : " + x);println ("y 的坐标点 : " + y);println ("z 的坐标点 : " + z);}
}object Test {def main(args: Array[String]) {val loc = new Location(10, 20, 15);// 移到一个新的位置loc.move(10, 10, 5);}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 的坐标点 : 20
y 的坐标点 : 30
z 的坐标点 : 20

Scala 重写一个非抽象方法,必须用override 修饰符。

class Person {var name = ""override def toString = getClass.getName + "[name=" + name + "]"
}class Employee extends Person {var salary = 0.0override def toString = super.toString + "[salary=" + salary + "]"
}object Test extends App {val fred = new Employeefred.name = "Fred"fred.salary = 50000println(fred)
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Employee[name=Fred][salary=50000.0]

Scala 单例对象

在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。

Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object 对象不能带参数。

当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。

单例对象实例

import java.io._class Point(val xc: Int, val yc: Int) {var x: Int = xcvar y: Int = ycdef move(dx: Int, dy: Int) {x = x + dxy = y + dy}
}object Test {def main(args: Array[String]) {val point = new Point(10, 20)printPointdef printPoint{println ("x 的坐标点 : " + point.x);println ("y 的坐标点 : " + point.y);}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 的坐标点 : 10
y 的坐标点 : 20

伴生对象实例

// 私有构造方法
class Marker private(val color:String) {println("创建" + this)override def toString(): String = "颜色标记:"+ color}// 伴生对象,与类名字相同,可以访问类的私有属性和方法
object Marker{private val markers: Map[String, Marker] = Map("red" -> new Marker("red"),"blue" -> new Marker("blue"),"green" -> new Marker("green"))def apply(color:String) = {if(markers.contains(color)) markers(color) else null}def getMarker(color:String) = {if(markers.contains(color)) markers(color) else null}def main(args: Array[String]) {println(Marker("red"))  // 单例函数调用,省略了.(点)符号  println(Marker getMarker "blue")  }
}

执行以上代码,输出结果为:

$ scalac Marker.scala 
$ scala Marker
创建颜色标记:red
创建颜色标记:blue
创建颜色标记:green
颜色标记:red
颜色标记:blue

15. Scala Trait(特征)

Scala Trait(特征) 相当于 Java 的接口,实际上它比接口功能强大。

与接口不同的是,它还可以定义属性和方法的实现。

一般情况下 Scala 的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。

Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:

trait Equal {def isEqual(x: Any): Booleandef isNotEqual(x: Any): Boolean = !isEqual(x)
}

以上 Trait(特征) 由两个方法组成:isEqualisNotEqual。isEqual 方法没有定义方法的实现,isNotEqual 定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征) 更像 Java 的抽象类。

以下演示了特征的完整实例:

trait Equal {def isEqual(x: Any): Booleandef isNotEqual(x: Any): Boolean = !isEqual(x)
}class Point(xc: Int, yc: Int) extends Equal {var x: Int = xcvar y: Int = ycdef isEqual(obj: Any) =obj.isInstanceOf[Point] &&obj.asInstanceOf[Point].x == x
}object Test {def main(args: Array[String]) {val p1 = new Point(2, 3)val p2 = new Point(2, 4)val p3 = new Point(3, 3)println(p1.isNotEqual(p2))println(p1.isNotEqual(p3))println(p1.isNotEqual(2))}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
false
true
true

特征构造顺序

特征也可以由构造器,由字段的初始化和其他特征体中的语句构成。这些语句在爱任何混入该特征的对象在构造时都会被执行。

构造器的执行顺序:

  • 调用超类的构造器;
  • 特征构造器在超累构造器之后、类构造器之前执行;
  • 特征由左到右被构造;
  • 每个特征当中,父特征先被构造;
  • 如果多个特征共有一个父特征,父特征不会被重复构造
  • 所有特征被构造完毕,子类被构造。

构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。

16. Scala 模式匹配

Scala 提供了强大的模式匹配机制,应用也非常广泛。

一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式,箭头符号 => 隔开了模式和表达式。

以下是一个简单的整型值模式匹配实例:

object Test {def main(args: Array[String]) {println(matchTest(3))}def matchTest(x: Int): String = x match {case 1 => "one"case 2 => "two"case _ => "many"}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
many

match 对应 Java 里的 switch,但是卸载选择器表达式之后。即:选择器 match {备选项}

match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的 case 不会继续匹配。

接下来我们来看一个不同数据类型的模式匹配:

object Test {def main(args: Array[String]) {println(matchTest("two"))println(matchTest("test"))println(matchTest(1))println(matchTest(6))}def matchTest(x: Any): Any = x match {case 1 => "one"case "two" => 2case y: Int => "scala.Int"case _ => "many"}
}

执行以上代码,输出结果:

$ scalac Test.scala 
$ scala Test
2
many
one
scala.Int

实例中第一个 case 对应树型数值 1,第二个 case 对应字符串值 two,第三个 case 对应类型模式,用于判断传入的值是否为整型,相比使用 isInstanceOf 来判断类型,使用模式匹配更好。第四个 case 表示默认的全匹配备选项,即没有找到其他匹配时的匹配项,类似 switch 中的 default。

使用样例类

使用了 case 关键字的类定义都是样例类(case classes),样例类是种特殊的类,经过优化以用于模式匹配。

以下是样例类的简单实例:

object Test {def main(args: Array[String]) {val alice = new Person("Alice", 25)val bob = new Person("Bob", 32)val charlie = new Person("Charlie", 32)for (person <- List(alice, bob, charlie)) {person match {case Person("Alice", 25) => println("Hi Alice!")case Person("Bob", 32) => println("Hi Bob!")case Person(name, age) =>println("Age: " + age + " year, name: " + name + "?")}}}// 样例类case class Person(name: String, age: Int)
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?

在生命样例类种,下面的过程自动发生了:

  • 构造器的每个参数都称为 val,除非显式被声明为 var,但是并不推荐这么做:
  • 在伴生对象种提供了 apply 方法,所以可以不使用 new 关键字就可构建对象;
  • 提供 unapply 方法使模式匹配可以工作;
  • 生成toString 、equals 、hashCode 和 copy 方法,除非显示给出这些方法的定义。

17. Scala 正则表达式

Scala 通过 scala.util.matching 包中的 Regex 类来支持正则表达式。以下示例演示了使用正则表达式查找单词 Scala:

import scala.util.matching.Regexobject Test {def main(args: Array[String]) {val pattern = "Scala".rval str = "Scala is Scalable and cool"println(pattern findFirstIn str)}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Some(Scala)

实例中使用 String 类的 r() 方法构造了一个 Regex 对象。

然后使用 findFirstIn 方法找到首个匹配项。

如果需要查看所有的匹配项可以使用 findAllIn 方法。

可以使用 mkString() 方法来连接正则表达式匹配结果的字符串,并可以使用管道(I) 来设置不同的模式:

import scala.util.matching.Regexobject Test {def main(args: Array[String]) {val pattern = new Regex("(S|s)cala")  // 首字母可以是大写 S 或小写 sval str = "Scala is scalable and cool"println((pattern findAllIn str).mkString(","))   // 使用逗号 , 连接返回结果}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Scala,scala

如果需要将匹配的文本替换为指定的关键词,可以使用 replaceFirstIn() 方法来替换第一个匹配项,使用 replaceAllIn() 方法替换所有匹配项,实例如下:

object Test {def main(args: Array[String]) {val pattern = "(S|s)cala".rval str = "Scala is scalable and cool"println(pattern replaceFirstIn(str, "Java"))}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Java is scalable and cool

18. Scala 异常处理

Scala 的异常处理和其他语言比如 Java 类似。

Scala 的方法可以通过抛出异常的方式来终止相关代码的运行,不必通过返回值。

抛出异常

Scala 跑出异常的方法和 Java 一样,使用 throw 方法,例如,抛出一个新的参数异常:

throw new IllegalArgumentException

捕获异常

异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。因此,在 catch 字句中,越具体的异常越要靠前,越普遍的异常越靠后。如果抛出的异常不在 catch 字句中,该异常则无法处理,会被升级到调用者处。捕捉异常的 catch 子句,语法与其他语言中不太一样。在 Scala 里,借用了模式匹配的思想来做异常的匹配,因此,在 catch 的代码里,是一系列 case 字句,如下所示:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOExceptionobject Test {def main(args: Array[String]) {try {val f = new FileReader("input.txt")} catch {case ex: FileNotFoundException =>{println("Missing file exception")}case ex: IOException => {println("IO Exception")}}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Missing file exception

catch 字句里的内容跟 match 里的case 是完全一样的。由于异常捕捉是按次序,如果最普遍的异常,Throwable,写在最前面,则在它后面的case 都捕捉不到,因此需要将它写在最后面 。

finally 语句

finally 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,实例如下:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOExceptionobject Test {def main(args: Array[String]) {try {val f = new FileReader("input.txt")} catch {case ex: FileNotFoundException => {println("Missing file exception")}case ex: IOException => {println("IO Exception")}} finally {println("Exiting finally...")}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Missing file exception
Exiting finally...

19. Scala 提取器(Extractor)

提取器是从传递给它的对象中提取处构造该对象的参数。

Scala 标准库包含了一些预定义的提取器。

Scala 提起器是一个带有 unapply 方法的对象。unapply 方法算是 apply 方法的反向操作:unapply 接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。

以下实例演示了邮件地址的提取器对象:

object Test {def main(args: Array[String]) {println ("Apply 方法 : " + apply("Zara", "gmail.com"));println ("Unapply 方法 : " + unapply("Zara@gmail.com"));println ("Unapply 方法 : " + unapply("Zara Ali"));}// 注入方法 (可选)def apply(user: String, domain: String) = {user +"@"+ domain}// 提取方法(必选)def unapply(str: String): Option[(String, String)] = {val parts = str split "@"if (parts.length == 2){Some(parts(0), parts(1))}else{None}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Apply 方法 : Zara@gmail.com
Unapply 方法 : Some((Zara,gmail.com))
Unapply 方法 : None

以上对象定义了两个方法:applyunapply 方法。通过 apply 方法 我们无需使用 new 操作就可以创建对象。所以你可以通过语句 Test(“Zara”, “gmail.com”) 来构造一个字符串 "Zara@gmail.com"

unapply 方法算是 apply 方法的反向操作:unapply 接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。实例中我们使用 Unapply 方法从对象中提取用户名和邮件地址的后缀。

实例中 unapply 方法在传入的字符串 不是邮箱地址时返回 None。代码演示如下:

unapply("Zara@gmail.com") 相等于 Some("Zara", "gmail.com")
unapply("Zara Ali") 相等于 None

提取器使用模式匹配

在实例化一个类时,可以带上0个或者多个的参数,编译器在实例化时会调用 apply 方法。我们可以在类和对象中都定义 apply 方法。

unapply 用于提取指定查找的值,它与 apply 的操作相反。当在提取器对象中使用 match 语句是,unapply 将自动执行,如下所示:

object Test {def main(args: Array[String]) {val x = Test(5)println(x)x match{case Test(num) => println(x + " 是 " + num + " 的两倍!")//unapply 被调用case _ => println("无法计算")}}def apply(x: Int) = x*2def unapply(z: Int): Option[Int] = if (z%2==0) Some(z/2) else None
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
10
10 是 5 的两倍!

20、Scala 文件 I/O

Scala 进行文件写操作,直接用的都是 java 中的 I/O 类(java.io.File):

import java.io._object Test {def main(args: Array[String]) {val writer = new PrintWriter(new File("test.txt" ))writer.write("hello world")writer.close()}
}

执行以上代码,会在你的当前目录下生产一个 test.txt 文件,文件内容为 “hello world”:

$ scalac Test.scala 
$ scala Test
$ cat test.txt 
hello world

从屏幕上读取用户输入

有时候需要接受用户在屏幕输入的指定来处理程序。实例如下:

import scala.io._
object Test {def main(args: Array[String]) {print("请输入菜鸟教程官网 : " )val line = StdIn.readLine()println("谢谢,你输入的是: " + line)}
}

Scala 2.11 后的版本 Console.readLine 已废弃,使用 scala.io.StdIn.readLine() 方法代替。

执行以上代码,屏幕上会显示如下信息:

$ scalac Test.scala 
$ scala Test
请输入菜鸟教程官网 : www.runoob.com
谢谢,你输入的是: www.runoob.com

从文件上读取内容

从文件读取内容非常简单。我们可以使用 Scala 的 Source 类及伴生对象来读取文件。以下示例演示了从 “test.txt” (之前已创建过)文件中读取内容:

import scala.io.Sourceobject Test {def main(args: Array[String]) {println("文件内容为:" )Source.fromFile("test.txt" ).foreach{print}}
}

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
文件内容为:
菜鸟教程

相关内容

热门资讯

诛仙最后的结局是不是暗示着碧瑶... 诛仙最后的结局是不是暗示着碧瑶活了?不是的``这个结局是很耐人寻味的``也许只是张小凡对碧瑶的想念才...
小说主公叫夏天,里面有个江海大... 小说主公叫夏天,里面有个江海大学,护花高手在都市 心在流浪写的 夏天 乔小乔 叶梦莹 孙馨馨
恶魔之塔第九层炎之森怎么过 恶魔之塔第九层炎之森怎么过恶魔之塔第九层的炎之森,以下是一些过关方法:1、观察关卡:仔细观察关卡布局...
描写开心的场景 描写开心的场景突然觉得天特别的蓝,那慵懒漂浮的云彩似乎活泼起来了,在暖风的追逐下,逐渐聚拢又分开。金...
我很爱他!日日夜夜的想他,怎么... 我很爱他!日日夜夜的想他,怎么办?我活的很累!!.从你说的来看,你需要冷静一点,太依赖不好.可能时间...
全球开园规模最大乐高乐园在上海... 作为全球开园规模最大的乐高乐园,上海乐高乐园5日正式开园。 业界人士认为,国际知名主题乐园相继加码布...
拜年的来历? 拜年的来历?那你是卖。... 那你是卖。 展开 传说远古时代有一种叫“年”的怪物,每逢腊月三十...
工作压力有利于成长的事例 工作压力有利于成长的事例 把李嘉诚的传记,推荐给您,我以前看过一多半,很有感触,希望可以帮助到您
苏武牧羊北海边的北海指的是哪里... 苏武牧羊北海边的北海指的是哪里?贝加尔湖,中国古代称为北海,位于俄罗斯西伯利亚南部简介贝加尔湖是亚欧...
购物狂电影的最后一幕那个弃婴 ... 购物狂电影的最后一幕那个弃婴 是怎么回事?是主角的小时候吧,讲她为什么是购物狂,因为她小时候被抛弃在...
方法决定成败 方法决定成败没错,方法决定成败,态度决定一切,心态决定效率。送你这几句名言,以资鼓励。
霞秋的意思?想表达晚年幸福? 霞秋的意思?想表达晚年幸福?霞秋的意思?想表达晚年幸福?... 霞秋的意思?想表达晚年幸福? 展...
关于“欣赏”的名言和事例 关于“欣赏”的名言和事例19世纪末,美国西部的密苏里有一个坏孩子,他偷偷地向邻居家的窗户扔石头,还把...
自己发的语音总是不敢去听,怎么... 自己发的语音总是不敢去听,怎么让自己勇敢去听呢?怎么说呢,其实你录下来的声音就是别人听到的,只是因为...
格物致知的真正意义是什么? 格物致知的真正意义是什么?提示:可以从文章内容中直接找到答案。? 参考答案:格物致知的真正...
醒悟中心卢卫斌老师谈强迫症的治... 醒悟中心卢卫斌老师谈强迫症的治疗为什么需要禅修练习  强迫症是一种复合型心理障碍,有其独特的障碍模型...
宣化那有好玩的地方 宣化那有好玩的地方有吗? 「莫等闲,白了少年头。空b切」
水深水浅东西涧 云去云来远近山... 水深水浅东西涧 云去云来远近山是什么意思  涧水或东或西时深时浅,山峦亦近云雾盘桓.  作品原文  ...
我的妹妹不可能那么可爱 讲的是... 我的妹妹不可能那么可爱 讲的是什么?就是有一个长的很可爱明明可以很受欢迎【也确实很受欢迎】的妹纸爱好...
童话诗有什么特点 童话诗有什么特点 童话诗(Fairy tale poem),故事诗的一种,现代诗的一种体裁。是以童话...