Scala入门
基础
Scala 不要求必须写;号作为一句代码的结束标志。
变量
Scala 中三种方式定义把变量
Scala允许您在声明它时决定变量是否是不可变的(只读)
- val
- var
- 延迟的 val
val 使用该关键字修饰的变量是不可变的(不可重新被赋值)。
val x = 10
var使用该关键字的的变量是可变的。
var x = 10
延迟val变量计算一次,第一次访问变量。只有vals可以是惰性变量。
object Main {
def main(args: Array[String]) {
val x = 10e20
println(x);
}
}
变量声明
Scala允许您在声明它时确定变量是不可变的(只读的)还是不可变的(读写的)。
val array: Array[String] = new Array(5)
例子
数组元素本身是可变的,因此可以修改元素:
注意:在声明时必须初始化val。
object Main {
def main(args: Array[String]) {
val array: Array[String] = new Array(5)
array = new Array(2)
array(0) = "Hello"
println(array )
}
}
可变变量
可变变量用关键字 var 声明,并且必须立即初始化。
object Main {
def main(args: Array[String]) {
var stockPrice: Double = 100.0
stockPrice = 200.0
println(stockPrice);
}
}
例子
下面的代码定义了一个具有不可变的名字和姓氏,但是一个可变的年龄的Person类
class Person(val name: String, var age: Int)
object Main {
def main(args: Array[String]) {
val p = new Person("Dean Wampler", 29)
println(p.name)
println(p.age )
p.age = 30
println(p.age )
}
}
注释
单行注释
单行注释用//开头,并继续到行尾:
// This is a single line comment
多行注释
多行注释以/开头,以/结束。
/*
This is a multiline comment:
*/
数据类型

Any
是所有类型的超类型,也称为顶级类 型。它定义了一些通用的方法如equals
、hashCode
和toString
。Any
有两个直接子类:AnyVal
和AnyRef
。
AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double
、Float
、Long
、Int
、Short
、Byte
、Char
、Unit
和Boolean
。Unit
是不带任何意义的值类型,它仅有一个实例可以像这样声明:()
。所有的函数必须有返回,所以说有时候Unit
也是有用的返回类型。
AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef
的子类型。如果Scala被应用在Java的运行环境中,AnyRef
相当于java.lang.Object
布尔类型
布尔类型限于文本true或文本false,如下面的示例所示。
val x = false
字符类型
字符常量用单引号编写,区别于使用双引号写的字符串常量。
// 字符
val x = 'X'
// 字符串
val x = "X"
字符串
Scala的String构建在Java的String上,并向Java的String添加了字符串插值等附加功能。
val hello = "Hello"
字符串插值
字符串插值是一种将字符串中的值与变量组合的机制。
Scala中的插值符号是在字符串的第一个双引号之前添加的s前缀。
然后可以使用美元符号运算符$引用变量。
// creating a String
val bookTitle = "Scala"
// String interpolation
println(s"Book Title is ${ bookTitle}" );
数字类型
Scala中的数字数据类型构成了Float和Double类型以及诸如Byte,Short,Int,Long和Char等整数数据类型。
下表显示Scala的数值数据类型。
数据类型 | 描述 |
---|---|
Byte | 从-128到127范围内的整数 |
Short | 从-32768到32767范围内的整数 |
Int | 从-2147483648到2147483647范围内的整数 |
Long | 从-9223372036854775808到9223372036854775807范围内的整数 |
Float | 最大正有限浮点是3.4028235 * 1038,最小正有限非零浮点是1.40 * 10-45 |
Double | 最大正有限双是1.7976931348623157 * 10308,最小正有限非零双是4.9 * 10-324 |
例子
Scala可以按顺序自动将数字从一种类型转换为另一种类型。

Byte . Short . Int . Long . Float . Double.
其中字节类型是最低的,并且可以转换为任何其他类型,如以下示例所示:
val x: Byte = 30
我们可以将x赋值为Short类型,如下例所示:
val y: Short = x
同样,我们可以将x赋值为Int,Long,Float,Double,Scala会自动转换数字,如下例所示:
val z: Double = y
Scala不允许以前面提到的顺序自动转换。
var hello: Byte = 30
// 执行下面自动类型顺序转换时会报错: Byte类型和Double类型不匹配
hello = 30.0
常量值
整数常量
数值常量范围表
目标类型 | 最低(含) | 最大(包括) |
---|---|---|
Long | ||
Int |
|
|
Short | ||
Char | 0 | |
Byte |
|
注意:对于长文本,需要在文本末尾附加L或l字符,除非将值分配给声明为Long的变量。 否则,推断Int
// 该段报错,默认识别为nt类型
var long = 922337203685477580
// 该段报错,即使变量定义为Long类型,也默认识别为Int
var l :Long = 922337203685477580
// 正确声明方式
var long = 922337203685477580L
var l: Long = 922337203685477580L
浮点常量
注意:
对于 Float 常量,在文字末尾附加F或f字符。否则,假定为Double
指数部分的格式为e或E,后跟可选的+或 - ,后跟一个或多个数字
.14
3.14
3.14f
3.14F
3.14d
3.14D
3e5
3E5
3.14e+5
3.14e-5
3.14e-5
3.14e-5f
3.14e-5F
3.14e-5d
3.14e-5D
布尔常量
val b1 = true
val b2 = false
字符常量
字符常量是可打印的Unicode字符或转义序列,写在单引号之间。
Unicode值介于0和255之间的字符可以由八进制转义表示,即反斜杠(\)后跟最多三个八进制字符的序列。
"A"
"\u0041" // "A" in Unicode
"\n"
"\012" // "\n" in octal
"\t"
字符串常量
在字符串中包含双引号“字符,必须使用\ 字符“转义"
"This is a\ntest"
"He said, \"SQL is for database!\""
"First\tSecond"
由双引号的三元组界定的字符串常量称为多行字符串常量
这些字符串可以覆盖几行。换行符将是字符串的一部分。它们可以包括任何字符,包括一个或两个双引号在一起,但不能三个在一起
"""This is a \ntest"""
"""He said, "SQL is for database!" """
"""First line\n
Second line\t
Fourth line"""
在代码中使用多行字符串时,要使用String.stripMargin缩进子字符串以进行正确的代码格式化。
它删除子字符串中的所有空格,直到并包括垂直条|的第一次出现。
要添加空格缩进,请将空格放在|后面。
val name = ray
def hello(name: String) = s"""Welcome!
Hello, $name!
* (Star!!)
|Hi.
| whitespace.""" .stripMargin
/* 输出结果
Welcome!
Hello,ray!
* (Star!!)
Hi.
whitespace.
*/
删除字符串中前缀或后缀,则有相应的前缀和stripSuffix方法
val name = "ray"
val s :String = s"""xxxGoodbye, ${name}yyy
| xxxCome again!yyy""".stripMargin .stripPrefix("xxx").stripSuffix("yyy")
/*
输出
Goodbye, rayyyy
xxxCome again!
*/
符号常量
Scala有符号类型,它们是内部连接的字符串,意味着具有相同“名称”的两个符号实际上是指内存中的同一个对象。
符号常量是单引号',后跟一个或多个数字,字母或下划线(“_”),但第一个字符不能是数字。
函数常量 (未解释清楚)
(i:Int,s:String)=> s + i 是Function2类型的函数文本[Int,String,String](返回String)
以下声明是等效的:
val f1: (Int,String) => String = (i, s) => s+i
val f2: Function2[Int,String,String] = (i, s) => s+i
元组常量
val t1: (Int,String) = (1, "two")
val t2: Tuple2[Int,String] = (1, "two")
// 元组参数调用
//表达式t._n从元组t中检索第n个项,从一个开始,不是零,遵循历史约定。
val t = ("Hello", 1, 2.3)
println( "Print the whole tuple: " + t )
println( "Print the first item: " + t._1 )
println( "Print the second item: " + t._2 )
println( "Print the third item: " + t._3 )
空值类型
Nothing
是所有类型的子类型,也称为底部类型。没有一个值是Nothing
类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
Null
是所有引用类型的子类型(即AnyRef
的任意子类型)。它有一个单例值由关键字null
所定义。Null
主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null
的替代方案。
选项
Option 允许我们在没有null“hack”的情况下显式地表达空值。
Option是一个抽象类,它的两个具体子类是Some,当我们有一个值,而None,当我们没有。
val stateCapitals = Map(
"Alabama" -> "Montgomery",
"Alaska" -> "Juneau",
"Wyoming" -> "Cheyenne")
println( "Get the capitals wrapped in Options:" )
println( "Alabama: " + stateCapitals.get("Alabama") )
println( "Wyoming: " + stateCapitals.get("Wyoming") )
println( "Unknown: " + stateCapitals.get("Unknown") )
println( "Get the capitals themselves out of the Options:" )
println( "Alabama: " + stateCapitals.get("Alabama").get )
println( "Wyoming: " + stateCapitals.get("Wyoming").getOrElse("Oops!") )
println( "Unknown: " + stateCapitals.get("Unknown").getOrElse("Oops2!") )
/* 输出
Get the capitals wrapped in Options:
Alabama: Some(Montgomery)
Wyoming: Some(Cheyenne)
Unknown: None
Get the capitals themselves out of the Options:
Alabama: Montgomery
Wyoming: Cheyenne
Unknown: Oops2!
*/
注意
Map.get方法返回一个Option [T]
,在这种情况下 T
是String。
通过返回一个选项,我们不能“忘记”我们必须验证返回的东西。
如果 Option
是 Some
,则 Some.get
返回值。
如果 Option
实际上是 None
,那么 None.get
将抛出一个NoSuchElementException
异常。
在最后两个println语句中的getOrElse
返回 Option
中的值,如果它是一个 Some
实例,或者返回传递给getOrElse
的参数,如果它是一个 None
实例。getOrElse
参数作为默认返回值。
范围Range
// 打印 1,2,3,4,5 包含上下限 左闭右闭
println( 1 to 5 )
// 打印 1,2,3,4 包含下限单不包含上限 左闭右开
println( 1 until 5 )
// 定义带步长,每四步打印一次
// 1,5,9,13,17
println( 1 to 20 by 4 )
/*
几个特殊例子
*/
val v3 = 1L to 10L by 3 // Long
// 1, 4, 7, 10
println(v3)
val v4 = 1.1f to 10.3f by 3.1f // Float with an interval != 1
// 1.1, 4.2, 7.2999997
println(v4)
val v5 = 1.1f to 10.3f by 0.5f // Float with an interval < 1
// 1.1, 1.6, 2.1, 2.6, 3.1, 3.6, 4.1, 4.6, 5.1, 5.6, 6.1, 6.6, 7.1, 7.6, 8.1, 8.6, 9.1, 9.6, 10.1
println(v5)
val v6 = 1.1 to 10.3 by 3.1 // Double
// 1.1, 4.2, 7.300000000000001
println(v6)
val v7 = 'a' to 'g' by 3 // Char
// a, d, g
println(v7)
val v8 = BigInt(1) to BigInt(10) by 3
// 1, 4, 7, 10
println(v8)
val v9 = BigDecimal(1.1) to BigDecimal(10.3) by 3.1
// 1.1, 4.2, 7.3
println(v9)
元组
元组是具有相同或不同类型的两个或更多个值的有序容器
与列表和数组不同,没有办法迭代元组中的元素
我们可以通过两种方式创建一个元组:
- 通过用逗号分隔的值写入值,并用一对括号括起来
- 通过使用关系运算符->
// 以下代码显示了一个包含Int,一个布尔值和一个String的元组,使用前一个方法。
val tuple = (1, false, "Scala")
// 以下代码显示了使用关系运算符创建的元组:
val tuple2 ="title" -> "Beginning Scala"
// 元组的单个元素可以通过其索引访问,其中第一个元素具有索引1。
val tuple = (1, false, "Scala")
// 以下代码显示了访问元组的第三个元素。
val third = tuple._3
单元类型
单元类型用于定义不返回数据的函数。它类似于Java中的void关键字
单元常量是一对空圆括号, ()。
def main(args: Array[String]) : Unit = {
}
语句
条件运算符
运算符 | 操作 | 描述 |
---|---|---|
&& | 与 | 运算符左侧和右侧的值为true。仅当左侧为真时,右侧才被计算。 |
|| | 或 | 左侧或右侧的至少一个值为true。仅当左边为假时才计算右侧。 |
> | 大于 | 左侧的值大于右侧的值。 |
>= | 大于或等于 | 左侧的值大于或等于右侧的值。 |
< | 少于 | 左侧的值小于右侧的值。 |
<= | 小于或等于 | 左侧的值小于或等于右侧的值。 |
== | 等于 | 左侧的值与右侧的值相同。 |
!= | 不等于 | 左侧的值与右侧的值不同。 |
注意
在Java中,==仅比较对象引用。它不会执行逻辑等同性检查,即比较字段值。使用 equals 方法。
Scala使用==作为逻辑等式,但它调用equals方法。当您想要比较引用,但不测试逻辑时,可以使用新的方法 eq
条件判断if
// 基础判断
if (exp) println("yes")
// 三元组判断方式
val i: Int = if (exp) 1 else 3
// 代码块嵌套
val i: Int = if (exp)
1
else {
val j = System.currentTimeMillis
(j % 100L).toInt
}
循环语句
for 循环
// 最基本for循环,类似于java1.5后支持的增强for循环
val books = List("Scala", "Groovy", "Java", "SQL", "CSS")
for (book<-books)
println(book)
// <- 这种方式也叫生成器表达式
for (i <- 1 to 10) println(i)
// 约束: 过滤值
// 我们可以添加if表达式来过滤我们想要保留的元素。
val dogBreeds = List("D", "Y", "D", "S", "G", "P")
for (breed <- dogBreeds
if breed.contains("D")
) println(breed)
//多约束
for (breed <- dogBreeds
if breed.contains("D")
if !breed.startsWith("Y")
) println(breed)
// 可变绑定
// 我们可以为表达式定义变量。
// 然后我们可以在你的for表达式的正文中重用这些变量。
val books = List("Scala", "Groovy", "Java", "SQL", "CSS")
for {
book <- books
bookVal = book.toUpperCase()
} println(bookVal) //bookVal没有声明为val,但是你仍然可以重用它
/*
Yielding
在Scala的for表达式中,我们可以使用yield关键字来生成新的集合。
从for表达式生成的集合的类型从迭代的集合的类型推断。
要在for循环中将值赋给我们的程序的另一部分,请使用yield关键字为表达式生成新的集合。
*/
val books = List("Scala", "Groovy", "Java", "SQL", "CSS")
var scalabooks = for{
book <-books
if book.contains("Scala")
}yield book
println(scalabooks);
while 循环
// 基本用法和其他语言一直
while(true){
println("hello")
}
// do-while 也与其他语言一直
do {
count += 1
println(count)
} while (count < 10)
异常捕获try语句
Scala中的异常处理以不同的方式实现,但它的行为与Java完全相同,并与现有的Java库无缝协作。
Scala中的所有异常都未选中;没有检查异常的概念。
抛出异常在Scala和Java中是一样的。
throw new Exception("some exception...")
// try/finally结构在Scala和Java中也是一样的,如下面的代码所示。
try {
throw newException("some exception...")
} finally{
println("This will always be printed")
}
// 异常捕获
// Scala中的异常可以在catch块中进行模式匹配,而不是为每个不同的异常提供单独的catch子句。
try {
file.write(stuff)
} catch{
case e:java.io.IOException => // handle IO Exception
case n:NullPointerException => // handle null pointer
}
模式匹配
def printNum(int: Int) {
int match {
case 0 => println("Zero")
case 1 => println("One")
// _表示默认匹配 default
case _ => println("more than one")
}
}
printNum(0)
printNum(1)
printNum(2)
// 匹配任何类型,传入object,判断类型
def test2(in: Any) = in match {
case s: String => "String, length "+s.length
case i: Int if i > 0 => "Natural Int"
case i: Int => "Another Int"
case a: AnyRef => a.getClass.getName
case _ => "null"
}