runoob-scala
https://www.runoob.com/scala/scala-tutorial.html
Scala 简介
Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。
Scala 运行在 Java 虚拟机上,并兼容现有的 Java 程序。
Scala 源代码被编译成 Java 字节码,所以它可以运行于 JVM 之上,并可以调用现有的 Java 类库。
Scala 特性
面向对象特性
Scala是一种纯面向对象的语言,每个值都是对象。对象的数据类型以及行为由类和特质描述。
类抽象机制的扩展有两种途径:一种途径是子类继承,另一种途径是灵活的混入机制。这两种途径能避免多重继承的种种问题。
函数式编程
Scala也是一种函数式语言,其函数也能当成值来使用。Scala提供了轻量级的语法用以定义匿名函数,支持高阶函数,允许嵌套多层函数,并支持柯里化。Scala的case class及其内置的模式匹配相当于函数式编程语言中常用的代数类型。
更进一步,程序员可以利用Scala的模式匹配,编写类似正则表达式的代码处理XML数据。
静态类型
Scala具备类型系统,通过编译时检查,保证代码的安全性和一致性。类型系统具体支持以下特性:
- 泛型类
- 协变和逆变
- 标注
- 类型参数的上下限约束
- 把类别和抽象类型作为对象成员
- 复合类型
- 引用自己时显式指定类型
- 视图
- 多态方法
扩展性
Scala的设计秉承一项事实,即在实践中,某个领域特定的应用程序开发往往需要特定于该领域的语言扩展。Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:
- 任何方法可用作前缀或后缀操作符
- 可以根据预期类型自动构造闭包。
并发性
Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱发收消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。
谁使用了 Scala
- 2009年4月,Twitter宣布他们已经把大部分后端程序从Ruby迁移到Scala,其余部分也打算要迁移。
- 此外,Wattzon已经公开宣称,其整个平台都已经是基于Scala基础设施编写的。
- 瑞银集团把Scala用于一般产品中。
- Coursera把Scala作为服务器语言使用。
Scala Web 框架
以下列出了两个目前比较流行的 Scala 的 Web应用框架:
接下来,我们可以从 Scala 官网地址 http://www.scala-lang.org/downloads 下载 Scala 二进制包,本教程我们将下载 2.11.7版本,如下图所示:
Scala 基础语法
如果你之前是一名 Java 程序员,并了解 Java 语言的基础知识,那么你能很快学会 Scala 的基础语法。
Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的。
我们可以认为 Scala 程序是对象的集合,通过调用彼此的方法来实现消息传递。接下来我们来理解下,类,对象,方法,实例变量的概念:
-
对象 - 对象有属性和行为。例如:一只狗的状属性有:颜色,名字,行为有:叫、跑、吃等。对象是一个类的实例。
-
类 - 类是对象的抽象,而对象是类的具体实例。
-
方法 - 方法描述的基本的行为,一个类可以包含多个方法。
-
字段 - 每个对象都有它唯一的实例变量集合,即字段。对象的属性通过给字段赋值来创建。
第一个 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 = 2
scala> println("Hello World!")
Hello World!
基本语法
Scala 基本语法需要注意以下几点:
-
区分大小写 - Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义。
-
类名 - 对于所有的类名的第一个字母要大写。
如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。
示例:class MyFirstScalaClass -
方法名称 - 所有的方法名称的第一个字母用小写。
如果若干单词被用于构成方法的名称,则每个单词的第一个字母应大写。
示例:def myMethodName() -
程序文件名 - 程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。
保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加".scala"为文件扩展名。 (如果文件名和对象名称不匹配,程序将无法编译)。
示例: 假设"HelloWorld"是对象的名称。那么该文件应保存为'HelloWorld.scala" -
def main(args: Array[String]) - Scala程序从main()方法开始处理,这是每一个Scala程序的强制程序入口部分。
Scala 关键字
下表列出了 scala 保留关键字,我们不能使用以下关键字作为变量:
abstract | case | catch | class |
def | do | else | extends |
false | final | finally | for |
forSome | if | implicit | import |
lazy | match | new | null |
object | override | package | private |
protected | return | sealed | super |
this | throw | trait | try |
true | type | val | var |
while | with | yield | |
- | : | = | => |
<- | <: | <% | >: |
# | @ |
Scala 注释
Scala 类似 Java 支持单行和多行注释。多行注释可以嵌套,但必须正确嵌套,一个注释开始符号对应一个结束符号。注释在 Scala 编译中会被忽略,实例如下:
object HelloWorld {
/* 这是一个 Scala 程序
* 这是一行注释
* 这里演示了多行注释
*/
def main(args: Array[String]) {
// 输出 Hello World
// 这是一个单行注释
println("Hello, world!")
}
}
空行和空格
一行中只有空格或者带有注释,Scala 会认为其是空行,会忽略它。标记可以被空格或者注释来分割。
换行符
Scala是面向行的语言,语句可以用分号(;)结束或换行符。Scala 程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅 有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。例如
val s = "菜鸟教程"; println(s)
Scala 包
定义包
Scala 使用 package 关键字定义包,在Scala将代码定义到某个包中有两种方式:
第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如:
package com.runoob
class HelloWorld
第二种方法有些类似 C#,如:
package com.runoob {
class HelloWorld
}
第二种方法,可以在一个文件中定义多个包。
引用
Scala 使用 import 关键字引用包。
import java.awt.Color // 引入Color
import 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 数据类型
Scala 与 Java有着相同的数据类型,下表列出了 Scala 支持的数据类型:
数据类型 | 描述 |
---|---|
Byte | 8位有符号补码整数。数值区间为 -128 到 127 |
Short | 16位有符号补码整数。数值区间为 -32768 到 32767 |
Int | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 |
Float | 32 位, IEEE 754 标准的单精度浮点数 |
Double | 64 位 IEEE 754 标准的双精度浮点数 |
Char | 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF |
String | 字符序列 |
Boolean | true或false |
Unit | 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
Null | null 或空引用 |
Nothing | Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。 |
Any | Any是所有其他类的超类 |
AnyRef | AnyRef类是Scala里所有引用类(reference class)的基类 |
上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。
Scala 基础字面量
Scala 非常简单且直观。接下来我们会详细介绍 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!"
"菜鸟教程官网:www.runoob.com"
多行字符串的表示方法
多行字符串用三个双引号来表示分隔符,格式为:""" ... """。
实例如下:
val foo = """菜鸟教程
www.runoob.com
www.w3cschool.cc
www.runnoob.com
以上三个地址都能访问"""
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 字符可以用一个八进制转义序列来表示,即反斜线‟\‟后跟 最多三个八进制。
在字符或字符串中,反斜线和后面的字符序列不能构成一个合法的转义序列将会导致 编译错误。
Scala 变量
变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。
基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,你可以在这些变量中存储整数,小数或者字母。
变量声明
在学习如何声明变量与常量之前,我们先来了解一些变量与常量。
- 一、变量: 在程序运行过程中其值可能发生改变的量叫做变量。如:时间,年龄。
- 二、常量 在程序运行过程中其值不会发生变化的量叫做常量。如:数值 3,字符'A'。
在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量。
声明变量实例如下:
var myVar : String = "Foo"
var myVar : String = "Too"
Scala 访问修饰符
Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。
如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
私有(Private)成员
用 private 关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。
实例
class Inner{
private def f(){
println("f")
}
class InnerMost{
f() // 正确
}
}
(new Inner).f() //错误
}
(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类 Inner 之内。
但在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。
Java 中允许这两种访问,因为它允许外部类访问内部类的私有成员。
保护(Protected)成员
在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用 protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。
实例
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 在同一包里。
公共(Public)成员
Scala 中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
实例
class Inner {
def f() { println("f") }
class InnerMost {
f() // 正确
}
}
(new Inner).f() // 正确因为 f() 是 public
}
Scala 运算符
一个运算符是一个符号,用于告诉编译器来执行指定的数学运算和逻辑运算。
Scala 含有丰富的内置运算符,包括以下几种类型:
-
算术运算符
-
关系运算符
-
逻辑运算符
-
位运算符
-
赋值运算符
Scala IF...ELSE 语句
if 语句
if 语句有布尔表达式及之后的语句块组成。
语法
if 语句的语法格式如下:
if(布尔表达式)
{
// 如果布尔表达式为 true 则执行该语句块
}
如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。
实例
实例
def main(args: Array[String]) {
var x = 10;
if( x < 20 ){
println("x < 20");
}
}
}
Scala 循环
循环类型
Scala 语言提供了以下几种循环类型。点击链接查看每个类型的细节。
循环类型 | 描述 |
---|---|
while 循环 | 运行一系列语句,如果条件为true,会重复运行,直到条件变为false。 |
do...while 循环 | 类似 while 语句区别在于判断循环条件之前,先执行一次循环的代码块。 |
for 循环 | 用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。 |
循环控制语句
循环控制语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Scala 以下几种循环控制语句:
Scala 不支持 break 或 continue 语句,但从 2.8 版本后提供了一种中断循环的方式,点击以下链接查看详情。
控制语句 | 描述 |
---|---|
break 语句 | 中断循环 |
实例
实例
def main(args: Array[String]) {
// 局部变量
var a = 10;
// while 循环执行
while( a < 20 ){
println( "Value of a: " + a );
a = a + 1;
}
}
}
实例
实例
def main(args: Array[String]) {
// 局部变量
var a = 10;
// do 循环
do{
println( "Value of a: " + a );
a = a + 1;
}while( a < 20 )
}
}
实例
以下是一个使用了 i to j 语法(包含 j)的实例:
实例
def main(args: Array[String]) {
var a = 0;
// for 循环
for( a <- 1 to 10){
println( "Value of a: " + a );
}
}
}
实例
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" );
}
}
Scala 方法与函数
Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。
class Test{
def m(x: Int) = x + 3
val f = (x: Int) => x + 3
}
实例
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
Scala 闭包
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 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
完整实例
实例
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
}
Scala 字符串
以下实例将字符串赋值给一个常量:
实例
val greeting: String = "Hello,World!"
def main(args: Array[String]) {
println( greeting )
}
}
以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)。
在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
但其他对象,如数组就是可变的对象。接下来我们会为大家介绍常用的 java.lang.String 方法。
创建字符串
创建字符串实例如下:
var greeting = "Hello World!";
或
var greeting:String = "Hello World!";
你不一定为字符串指定 String 类型,因为 Scala 编译器会自动推断出字符串的类型为 String。
当然我们也可以直接显示的声明字符串为 String 类型,如下实例:
实例
val greeting: String = "Hello, World!"
def main(args: Array[String]) {
println( greeting )
}
}
String 方法
下表列出了 java.lang.String 中常用的方法,你可以在 Scala 中使用:
序号 | 方法及描述 |
---|---|
1 |
char charAt(int index) 返回指定位置的字符 |
2 |
int compareTo(Object o) 比较字符串与对象 |
3 |
int compareTo(String anotherString) 按字典顺序比较两个字符串 |
4 |
int compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写 |
5 |
String concat(String str) 将指定字符串连接到此字符串的结尾 |
6 |
boolean contentEquals(StringBuffer sb) 将此字符串与指定的 StringBuffer 比较。 |
7 |
static String copyValueOf(char[] data) 返回指定数组中表示该字符序列的 String |
8 |
static String copyValueOf(char[] data, int offset, int count) 返回指定数组中表示该字符序列的 String |
9 |
boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束 |
10 |
boolean equals(Object anObject) 将此字符串与指定的对象比较 |
11 |
boolean equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写 |
12 |
byte getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中 |
13 |
byte[] getBytes(String charsetName 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中 |
14 |
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此字符串复制到目标字符数组 |
15 |
int hashCode() 返回此字符串的哈希码 |
16 |
int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引 |
17 |
int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索 |
18 |
int indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引 |
19 |
int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 |
20 |
String intern() 返回字符串对象的规范化表示形式 |
21 |
int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引 |
22 |
int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索 |
23 |
int lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引 |
24 |
int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索 |
25 |
int length() 返回此字符串的长度 |
26 |
boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式 |
27 |
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等 |
28 |
boolean regionMatches(int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等 |
29 |
String replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的 |
30 |
String replaceAll(String regex, String replacement 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串 |
31 |
String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串 |
32 |
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串 |
33 |
String[] split(String regex, int limit) 根据匹配给定的正则表达式来拆分此字符串 |
34 |
boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开始 |
35 |
boolean startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。 |
36 |
CharSequence subSequence(int beginIndex, int endIndex) 返回一个新的字符序列,它是此序列的一个子序列 |
37 |
String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串 |
38 |
String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串 |
39 |
char[] toCharArray() 将此字符串转换为一个新的字符数组 |
40 |
String toLowerCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为小写 |
41 |
String toLowerCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为小写 |
42 |
String toString() 返回此对象本身(它已经是一个字符串!) |
43 |
String toUpperCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为大写 |
44 |
String toUpperCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为大写 |
45 |
String trim() 删除指定字符串的首尾空白符 |
46 |
static String valueOf(primitive data type x) 返回指定类型参数的字符串表示形式 |
Scala 数组
Scala 语言中提供的数组是用来存储固定大小的同类型元素,数组对于每一门编辑应语言来说都是重要的数据结构之一。
声明数组变量并不是声明 number0、number1、...、number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来表示一个个单独的变量。数组中某个指定的元素是通过索引来访问的。
数组的第一个元素索引为0,最后一个元素的索引为元素总数减1。
声明数组
以下是 Scala 数组声明的语法格式:
var z:Array[String] = new Array[String](3)
或
var z = new Array[String](3)
以上语法中,z 声明一个字符串类型的数组,数组长度为 3 ,可存储 3 个元素。我们可以为每个元素设置值,并通过索引来访问每个元素,如下所示:
z(0) = "Runoob"; z(1) = "Baidu"; z(4/2) = "Google"
最后一个元素的索引使用了表达式 4/2 作为索引,类似于 z(2) = "Google"。
我们也可以使用以下方式来定义一个数组:
var z = Array("Runoob", "Baidu", "Google")
Scala Collection
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
Scala 集合分为可变的和不可变的集合。
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
接下来我们将为大家介绍几种常用集合类型的应用:
序号 | 集合及描述 |
---|---|
1 | Scala List(列表)
List的特征是其元素以线性方式存储,集合中可以存放重复对象。 参考 API文档 |
2 | Scala Set(集合)
Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 参考 API文档 |
3 | Scala Map(映射)
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 参考 API文档 |
4 | Scala 元组
元组是不同类型的值的集合 |
5 | Scala Option
Option[T] 表示有可能包含值的容器,也可能不包含值。 |
6 | Scala Iterator(迭代器)
迭代器不是一个容器,更确切的说是逐一访问容器内元素的方法。 |
实例
以下代码判断,演示了所有以上集合类型的定义实例:
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 Iterator(迭代器)
Scala Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。
迭代器 it 的两个基本操作是 next 和 hasNext。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
让迭代器 it 逐个返回所有元素最简单的方法是使用 while 循环:
实例
def main(args: Array[String]) {
val it = Iterator("Baidu", "Google", "Runoob", "Taobao")
while (it.hasNext){
println(it.next())
}
}
}
Scala Iterator 常用方法
下表列出了 Scala Iterator 常用的方法:
序号 | 方法及描述 |
---|---|
1 |
def hasNext: Boolean 如果还有可返回的元素,返回true。 |
2 |
def next(): A 返回迭代器的下一个元素,并且更新迭代器的状态 |
3 |
def ++(that: => Iterator[A]): Iterator[A] 合并两个迭代器 |
4 |
def ++[B >: A](that :=> GenTraversableOnce[B]): Iterator[B] 合并两个迭代器 |
5 |
def addString(b: StringBuilder): StringBuilder 添加一个字符串到 StringBuilder b |
6 |
def addString(b: StringBuilder, sep: String): StringBuilder 添加一个字符串到 StringBuilder b,并指定分隔符 |
7 |
def buffered: BufferedIterator[A] 迭代器都转换成 BufferedIterator |
8 |
def contains(elem: Any): Boolean 检测迭代器中是否包含指定元素 |
9 |
def copyToArray(xs: Array[A], start: Int, len: Int): Unit 将迭代器中选定的值传给数组 |
10 |
def count(p: (A) => Boolean): Int 返回迭代器元素中满足条件p的元素总数。 |
11 |
def drop(n: Int): Iterator[A] 返回丢弃前n个元素新集合 |
12 |
def dropWhile(p: (A) => Boolean): Iterator[A] 从左向右丢弃元素,直到条件p不成立 |
13 |
def duplicate: (Iterator[A], Iterator[A]) 生成两个能分别返回迭代器所有元素的迭代器。 |
14 |
def exists(p: (A) => Boolean): Boolean 返回一个布尔值,指明迭代器元素中是否存在满足p的元素。 |
15 |
def filter(p: (A) => Boolean): Iterator[A] 返回一个新迭代器 ,指向迭代器元素中所有满足条件p的元素。 |
16 |
def filterNot(p: (A) => Boolean): Iterator[A] 返回一个迭代器,指向迭代器元素中不满足条件p的元素。 |
17 |
def find(p: (A) => Boolean): Option[A] 返回第一个满足p的元素或None。注意:如果找到满足条件的元素,迭代器会被置于该元素之后;如果没有找到,会被置于终点。 |
18 |
def flatMap[B](f: (A) => GenTraversableOnce[B]): Iterator[B] 针对迭代器的序列中的每个元素应用函数f,并返回指向结果序列的迭代器。 |
19 |
def forall(p: (A) => Boolean): Boolean 返回一个布尔值,指明 it 所指元素是否都满足p。 |
20 |
def foreach(f: (A) => Unit): Unit 在迭代器返回的每个元素上执行指定的程序 f |
21 |
def hasDefiniteSize: Boolean 如果迭代器的元素个数有限则返回 true(默认等同于 isEmpty) |
22 |
def indexOf(elem: B): Int 返回迭代器的元素中index等于x的第一个元素。注意:迭代器会越过这个元素。 |
23 |
def indexWhere(p: (A) => Boolean): Int 返回迭代器的元素中下标满足条件p的元素。注意:迭代器会越过这个元素。 |
24 |
def isEmpty: Boolean 检查it是否为空, 为空返回 true,否则返回false(与hasNext相反)。 |
25 |
def isTraversableAgain: Boolean Tests whether this Iterator can be repeatedly traversed. |
26 |
def length: Int 返回迭代器元素的数量。 |
27 |
def map[B](f: (A) => B): Iterator[B] 将 it 中的每个元素传入函数 f 后的结果生成新的迭代器。 |
28 |
def max: A 返回迭代器迭代器元素中最大的元素。 |
29 |
def min: A 返回迭代器迭代器元素中最小的元素。 |
30 |
def mkString: String 将迭代器所有元素转换成字符串。 |
31 |
def mkString(sep: String): String 将迭代器所有元素转换成字符串,并指定分隔符。 |
32 |
def nonEmpty: Boolean 检查容器中是否包含元素(相当于 hasNext)。 |
33 |
def padTo(len: Int, elem: A): Iterator[A] 首先返回迭代器所有元素,追加拷贝 elem 直到长度达到 len。 |
34 |
def patch(from: Int, patchElems: Iterator[B], replaced: Int): Iterator[B] 返回一个新迭代器,其中自第 from 个元素开始的 replaced 个元素被迭代器所指元素替换。 |
35 |
def product: A 返回迭代器所指数值型元素的积。 |
36 |
def sameElements(that: Iterator[_]): Boolean 判断迭代器和指定的迭代器参数是否依次返回相同元素 |
37 |
def seq: Iterator[A] 返回集合的系列视图 |
38 |
def size: Int 返回迭代器的元素数量 |
39 |
def slice(from: Int, until: Int): Iterator[A] 返回一个新的迭代器,指向迭代器所指向的序列中从开始于第 from 个元素、结束于第 until 个元素的片段。 |
40 |
def sum: A 返回迭代器所指数值型元素的和 |
41 |
def take(n: Int): Iterator[A] 返回前 n 个元素的新迭代器。 |
42 |
def toArray: Array[A] 将迭代器指向的所有元素归入数组并返回。 |
43 |
def toBuffer: Buffer[B] 将迭代器指向的所有元素拷贝至缓冲区 Buffer。 |
44 |
def toIterable: Iterable[A] Returns an Iterable containing all elements of this traversable or iterator. This will not terminate for infinite iterators. |
45 |
def toIterator: Iterator[A] 把迭代器的所有元素归入一个Iterator容器并返回。 |
46 |
def toList: List[A] 把迭代器的所有元素归入列表并返回 |
47 |
def toMap[T, U]: Map[T, U] 将迭代器的所有键值对归入一个Map并返回。 |
48 |
def toSeq: Seq[A] 将代器的所有元素归入一个Seq容器并返回。 |
49 |
def toString(): String 将迭代器转换为字符串 |
50 |
def zip[B](that: Iterator[B]): Iterator[(A, B) 返回一个新迭代器,指向分别由迭代器和指定的迭代器 that 元素一一对应而成的二元组序列 |
Scala 类和对象
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
我们可以使用 new 关键字来创建类的对象,实例如下:
实例
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点: " + x);
println ("y 的坐标点: " + y);
}
}
def main(args: Array[String]) {
val pt = new Point(10, 20);
// 移到一个新的位置
pt.move(10, 10);
}
}
Scala 继承
Scala继承一个基类跟Java很相似, 但我们需要注意以下几点:
- 1、重写一个非抽象方法必须使用override修饰符。
- 2、只有主构造函数才可以往基类的构造函数里写参数。
- 3、在子类中重写超类的抽象方法时,你不需要使用override关键字。
接下来让我们来看个实例:
实例
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("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 = zc
def move(dx: Int, dy: Int, dz: Int) {
x = x + dx
y = y + dy
z = z + dz
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
println ("z 的坐标点 : " + z);
}
}
Scala Trait(特征)
Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。
Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:
实例
def isEqual(x: Any): Boolean
def isNotEqual(x: Any): Boolean = !isEqual(x)
}
Scala 模式匹配
Scala 提供了强大的模式匹配机制,应用也非常广泛。
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
以下是一个简单的整型值模式匹配实例:
实例
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
Scala 正则表达式
Scala 通过 scala.util.matching 包中的 Regex 类来支持正则表达式。以下实例演示了使用正则表达式查找单词 Scala :
实例
object Test {
def main(args: Array[String]) {
val pattern = "Scala".r
val str = "Scala is Scalable and cool"
println(pattern findFirstIn str)
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Some(Scala)
Scala 异常处理
Scala 的异常处理和其它语言比如 Java 类似。
Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
抛出异常
Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:
throw new IllegalArgumentException
捕获异常
异常捕捉的机制与其他语言中一样,如果有异常发生,catch 字句是按次序捕捉的。因此,在 catch 字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在 catch 字句中,该异常则无法处理,会被升级到调用者处。
捕捉异常的 catch 子句,语法与其他语言中不太一样。在 Scala 里,借用了模式匹配的思想来做异常的匹配,因此,在 catch 的代码里,是一系列 case 字句,如下例所示:
实例
import java.io.FileNotFoundException
import java.io.IOException
object 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")
}
}
}
}
Scala 提取器(Extractor)
提取器是从传递给它的对象中提取出构造该对象的参数。
Scala 标准库包含了一些预定义的提取器,我们会大致的了解一下它们。
Scala 提取器是一个带有unapply方法的对象。unapply方法算是apply方法的反向操作:unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。
以下实例演示了邮件地址的提取器对象:
实例
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
}
}
}
Scala 文件 I/O
Scala 进行文件写操作,直接用的都是 java中 的 I/O 类 (java.io.File):
实例
object Test {
def main(args: Array[String]) {
val writer = new PrintWriter(new File("test.txt" ))
writer.write("菜鸟教程")
writer.close()
}
}
执行以上代码,会在你的当前目录下生产一个 test.txt 文件,文件内容为"菜鸟教程":
$ scalac Test.scala
$ scala Test
$ cat test.txt
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库