Scala • basis
catalogue
- 第一个 Scala 程序:Hello World
- 脚本形式
- 交互式编程
- other tips
- 数据类型
- 字符串插值
- 基本类型
- Byte
- Short
- Int
- Long
- Float
- Double
- Char
- String
- 多行字符串的表示方法
- Boolean
- 集合类型
- List
- Set
- Map
- Array
- Tuple
- Option
- Either
- Try
- 特殊类型
- Unit
- Null
- Nothing
- Any
- AnyRef
- 转义字符
- \b
- \t
- \n
- \f
- \r
- \ "
- \ '
- \ \
- 变量
- 访问修饰符
- 私有(Private)成员
- 保护(Protected)成员
- 公共(Public)成员
- 包级私有访问(Package-Private)
- 伴生对象和伴生类
- 运算符
- 算术运算符
- + - * / %
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- IF...ELSE 语句
- if 语句
- if...else 语句
- if...else if...else 语句
- if...else 嵌套语句
- 循环
- while 循环
- do...while 循环
- for循环
- 语法
- i to j 语法(包含 j)
- i until j 语法(不包含 j)
- other tips
- for 循环集合
- for 循环过滤
- for 使用 yield
- 方法与函数
- 方法定义
- 方法调用
- 传名调用 传值调用
- 可变参数
- 递归函数
- 默认参数值
- 高阶函数
- 匿名函数
- other tips
- 闭包
- 闭包作为返回值
- 字符串
- String Builder 类
- 字符串长度
- 字符串连接
- 常用方法
- 基本查询方法
- 子字符串方法
- 查找方法
- 比较方法
- 转换方法
- 去除空格
- 替换方法
- 分割方法
- 转换其他类型
- 集合操作方法(通过隐式转换)
- 数组
- 声明数组
- 设置值
- 多维数组
- 常用方法
- 集合
- List(列表)
- Set(集合)
- Map(映射)
- Tuple(元组)
- Option(选项)
- Iterator(迭代器)
- 类和对象
- Trait(特征)
- 模式匹配
- 异常处理
- 文件 I/O
第一个 Scala 程序:Hello World
脚本形式
建一个.txt
文件,用记事本打开,写入下方代码,写完Ctrl+S
保存,把后缀从.txt
改成.scala
,
object HelloWorld {def main(args: Array[String]): Unit = {println("Hello, world!")}
}
按顺序使用以下命令,注意不要进入scala
,要在系统终端下,所建.scala
文件所在目录下,执行以下代码
$ scalac HelloWorld.scala // 把源码编译为字节码
$ scala HelloWorld // 把字节码放到虚拟机中解释运行
比较快的是,直接在终端打开该目录
scala
命令也可以运行,不过严谨一点就是先用scalac
命令产生.class
文件,然后再用scala
命令运行,注意产生的.class
文件的名字是根据所写代码的类名产生的,不是你命名的.scala
文件哦~
交互式编程
以下命令需要进入scala
,scala
语法和java
很像,最大区别是scala
语句末尾的分号是可选的,但如果一行里写多个语句,那么分号是需要的。下面的实践作为初步了解scala
。
scala> 1 + 1
res0: Int = 2scala> println("Hello World!")
Hello World!
other tips
类名第一个字母大写,方法名第一个字母小写。
注释:
object HelloWorld {/* 这是一个 Scala 程序* 这是一行注释* 这里演示了多行注释*/def main(args: Array[String]) {// 输出 Hello World// 这是一个单行注释println("Hello, world!") }
}
//定义包
package com.runoob
//引用包
import java.awt.Color // 引入Color
//引入包中的几个成员,使用.{}
import java.awt.{Color, Font}
数据类型
字符串插值
语法是s"字符串内容 $变量名 其他内容"
// 需要花括号的情况
println(s"明年年龄: ${age + 1}") // 表达式
println(s"${name}的年龄") // 避免与后续文本混淆
val byteValue: Byte = 127
println(s"Byte Value: $byteValue")
基本类型
Byte
Short
Int
Long
最后加个L
Float
如果浮点数后面有 f
或者 F
后缀时,表示这是一个 Float 类型,否则就是一个 Double 类型的。
Double
Char
String
多行字符串的表示方法
多行字符串用三个双引号来表示分隔符,格式为:""" ... """
。
val foo = """菜鸟教程
www.runoob.com
www.runnoob.com
以上三个地址都能访问"""
Boolean
集合类型
List
不可变链表,即所有操作都只能返回新链表,不会改变也不能改变原链表
Set
不可变集合,即所有操作都只能返回新集合,不会改变也不能改变原集合
Map
不可变键值对集合,道理同上
Array
可变数组
Tuple
可包含不同类型元素的不可变容器
Option
代表有可能含有值或为空的容器,应用于处理可能缺失的值的场景,如查询可能不存在的记录
Either
Option
只能表示成功/失败,没有错误信息,而Either
可以提供详细的错误信息
// ❌ Option只能表示成功/失败,没有错误信息
def findUser(id: Int): Option[User] = {if (id > 0) Some(User("Alice", 25, "alice@example.com"))else None // 为什么失败?不知道!
}// ✅ Either可以提供详细的错误信息
def findUserEither(id: Int): Either[String, User] = {if (id <= 0) Left("用户ID必须为正数")else if (id > 1000) Left("用户ID超出范围")else Right(User("Alice", 25, "alice@example.com"))
}
Try
处理操作结果可能成功或失败的容器,提供了一种更优雅的方式来封装和处理可能抛出异常的操作。Try
类型位于 Scala 标准库的 scala.util
包中,使用前先import scala.util.Try
导入包~
特殊类型
Unit
表示无值,相当于Java中的 void
Null
表示所有引用类型的空值
Nothing
表示无返回值类型,是所有类型的子类型
nothingValue is not printed because it throws an exception
Any
所有类型的超类型
AnyRef
所有引用类型的超类型,等价于Java中的 Object
转义字符
转义字符以反斜杠 \
开头,紧跟一个特定的字符,表示某种特殊含义或效果。
\b
\b
(退格符): 使光标回退一个位置,是否删除退格符前的字符取决于终端或输出设备。
\t
\t
(制表符): 插入一个水平制表符,相当于一定数量的空格。"Hello\tWorld"
会在 "Hello"
和 "World"
之间插入一个制表符,通常是四个或八个空格。
\n
\n
(换行符): 移动到下一行开始新的输出。"Hello\nWorld"
会把 "Hello"
和 "World"
分成两行显示。
\f
\f
(换页符): 插入一个换页符,通常用来控制打印机换页,但在控制台输出中一般没有明显效果。
\r
\r
(回车符): 回到当前行的开头。"Hello\rWorld"
会覆盖掉当前行的内容,显示 "World"
。
\ "
\"
(双引号): 插入一个双引号。"He said, \"Hello, World!\""
显示 He said, "Hello, World!"
。
\ ’
\'
(单引号): 插入一个单引号。
\ \
变量
在 Scala 中,使用关键词 “var
” 声明变量,使用关键词 “val
” 声明常量。
变量的类型在变量名之后等号之前声明,如val age: Int = 10
,但Scala 的类型推断机制可以自动确定变量的类型,因此我们可以省略类型声明,如val age = 10
。类型推断使代码更加简洁,但在复杂场景中,显式声明类型可以避免混淆。
变量名通常第一个单词以小写字母开头。
尽量不要使用以下划线开头的变量名,这通常用于标识内部或私有变量。
同时声明多个变量,如:
访问修饰符
私有(Private)成员
scala:同一个类内
java:可以不同类,即允许外部类访问
(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类 Inner 之内。但在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。Java 中允许这两种访问,因为它允许外部类访问内部类的私有成员。
class Outer{class Inner{private def f(){println("f")}class InnerMost{f() // 正确}}(new Inner).f() //错误
}
保护(Protected)成员
scala:该类的子类
java:同一个包的类
在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用 protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。
下例中,Sub 类对 f 的访问没有问题,因为 f 在 Super 中被声明为 protected,而 Sub 是 Super 的子类。相反,Other 对 f 的访问不被允许,因为 other 没有继承自 Super。而后者在 java 里同样被认可,因为 Other 与 Sub 在同一包里。
package p {class Super {protected def f() {println("f")}}class Sub extends Super {f()//正确}class Other {(new Super).f() //错误}
}
公共(Public)成员
Scala 中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
包级私有访问(Package-Private)
Scala还支持包级私有访问修饰符,用于限制成员只能在特定包内访问。可以通过将private关键字后跟一个包名来实现。这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。格式为:
private[x] 或 protected[x]
例子中,类 Navigator 被标记为 private[bobsrockets] 就是说这个类对包含在 bobsrockets 包里的所有的类和对象可见。比如说,从 Vehicle 对象里对 Navigator 的访问是被允许的,因为对象 Vehicle 包含在包 launch 中,而 launch 包在 bobsrockets 中,相反,所有在包 bobsrockets 之外的代码都不能访问类 Navigator。
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}}
}
伴生对象和伴生类
在 Scala 中,伴生对象和伴生类可以互相访问对方的私有成员。伴生对象和伴生类是同名且在同一文件中的类和对象。
class CompanionExample {private val privateField: String = "I am private in class"
}object CompanionExample {def accessPrivateField(example: CompanionExample): String = {example.privateField // 伴生对象可以访问类的 private 成员}
}object Main {def main(args: Array[String]): Unit = {val example = new CompanionExampleprintln(CompanionExample.accessPrivateField(example))}
}
运算符
算术运算符
+ - * / %
+ - * / %
分别是加、减、乘、除、取余
加
减
乘
除
取余
关系运算符
结果是true
或者false
例如:
逻辑运算符
位运算符
位运算符用来对二进制位进行操作,~,&,|,^
分别为取反,按位与,按位或,按位异或运算
取反:0变1,1变0
按位与:都1才1
按位或:有1则1
按位异或:不同则1
赋值运算符
优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。
IF…ELSE 语句
if 语句
if…else 语句
if…else if…else 语句
if…else 嵌套语句
循环
while 循环
while 循环的关键点是循环可能一次都不会执行。
do…while 循环
不像 while 循环在循环头部测试循环条件, Scala 语言中,do…while 循环是在循环的尾部检查它的条件,所以 do…while 循环会确保至少执行一次循环。
10,不判断😴,首次是直接执行,变11
11,符合条件✅,执行,变12
12,符合条件✅,执行,变13
13,符合条件✅,执行,变14
14,符合条件✅,执行,变15
15,不符合条件❌,循环结束
所以第二个循环中,已经是15开始了,是不符合a > 100
的条件的,但 do…while 循环是先执行后判断,所以也输出了。
for循环
for 循环允许您编写一个执行指定次数的循环控制结构。
语法
i to j 语法(包含 j)
i until j 语法(不包含 j)
other tips
在 for 循环 中你可以使用分号来设置多个区间,它将迭代给定区间所有的可能值。
例子一:
例子二:
for 循环集合
迭代所有集合的元素
例子中,for循环中的a
是临时变量,不需要提前定义。
for 循环过滤
可以使用分号来为表达式添加一个或多个的过滤条件
for 使用 yield
可以将 for 循环的返回值作为一个变量存储
方法与函数
方法定义
Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。Scala 中使用 val
语句可以定义函数,def
语句定义方法。
class Test{def m(x: Int) = x + 3 //方法val f = (x: Int) => x + 3 //函数
}
注意:有些翻译上函数(function)与方法(method)是没有区别的。
如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型于是也是一个抽象类型。
格式:
def functionName ([参数列表]) : [return type] = {function bodyreturn [expr]
}
例子:
如果方法没有返回值,可以返回为 Unit
,这个类似于 Java 的 void
, 实例如下:
方法调用
传名调用 传值调用
Scala的解释器在解析函数参数(function arguments)时有两种方式:
语法区别:传名调用在参数类型前加 =>
选择依据:根据参数是否必然被使用、计算成本来决定
传值调用例子:
// 传值调用:参数在传入前就计算好
def callByValue(x: Int): Unit = {// x 在这里已经是计算好的值println("第一次使用 x: " + x)println("第二次使用 x: " + x) // 直接使用缓存的值
}// 测试
val result = callByValue({println("计算参数值")1 + 2 // 这个表达式会立即计算
})
结果:
传名调用例子:
// 传名调用:参数在每次使用时才计算
def callByName(x: => Int): Unit = { // 注意 => 符号// 第一次使用x时执行表达式println("第一次使用 x: " + x) // 这里才计算 x,执行: {println("计算"); 1+2}// 第二次使用x时再次执行表达式 println("第二次使用 x: " + x) // 这里再次计算 x,执行: {println("计算"); 1+2}
}// 测试
val result = callByName({println("计算参数值")1 + 2 // 这个表达式会延迟计算
})
结果:
可变参数
Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:
object Variable {def main(args: Array[String]) {printStrings("All","is","well")}def printStrings(args:String*) = {for(arg <- args){println(arg)}}
}
递归函数
递归函数意味着函数可以调用它本身。
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)}
}
结果:
默认参数值
Scala 可以为函数参数指定默认参数值,使用了默认参数,你在调用函数的过程中可以不需要传递参数,这时函数就会调用它的默认参数值,如果传递了参数,则传递值会取代默认值。
object Default{def main(args: Array[String]){println( "defaultaddInt(6, 7, 8)的返回值 : " + defaultaddInt(6, 7, 8) ) println(s"defaultaddInt(c = 2)的返回值:${defaultaddInt(c = 2)}")}def defaultaddInt(c:Int, a:Int=5, b:Int=7 ) : Int ={var sum:Int = 0 sum = a + b + creturn sum}
}
高阶函数
高阶函数(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() + "]"}
输出结果为:
匿名函数
Scala 中定义匿名函数的语法很简单,箭头=>
左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。
other tips
一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数。
例如:
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 );}
}
闭包
闭包特性:内部函数捕获外部变量。就是使用了函数外定义的变量,不完全是函数内部的定义的临时变量。产生的效果是当外部变量的值改变时,闭包内的计算结果也相应改变。
下面例子中我们引入一个自由变量 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
}
闭包作为返回值
即返回一个引用了外部变量的函数。
此处重点补充一种代码的常用写法➡️当返回类型是函数时:
如果接收多个参数:(参数类型1, 参数类型2, ...) => 返回类型
,(String, Int) => Int
表示一个接受 String 和 Int 两个参数并返回 Int 的函数类型。
例子:
def makeAdder(adder: Int): Int => Int = {(x: Int) => x + adder
}
以上例子更简洁的写法为:这里利用了scala能够进行类型推断的特点
def makeAdder(adder: Int) = (x: Int) => x + adder
字符串
String Builder 类
在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
如果你需要创建一个可以修改的字符串,可以使用 String Builder 类。下面例子中有几个注意点:
- 为什么不能用
buf = 'a'
赋值?答:因为 buf 是一个 对象引用,不是基本数据类型,要调用对象的方法来修改内容,调用+=
方法添加字符,类似java中的buf.append('a');
。 - 为什么
buf ++= “bcdef"
不能用+=
,有什么区别吗?答:+=
添加单个字符,++=
添加字符串或集合。
object Test {def main(args: Array[String]) {val buf = new StringBuilder;buf += 'a'buf ++= "bcdef"println( "buf is : " + buf.toString );}
}
结果:
字符串长度
使用 length()
方法来获取字符串长度,如:
字符串连接
concat()
方法:只能连接两个字符串
+
号:可以连接多个
常用方法
基本查询方法
子字符串方法
查找方法
比较方法
转换方法
去除空格
val str = " Hello World "println(str.trim) // "Hello World" - 去除首尾空白
println(" hello ".strip()) // "hello" - 去除首尾空白(Scala推荐)
println(" hello ".stripLeading()) // "hello " - 去除开头空白
println(" hello ".stripTrailing()) // " hello" - 去除结尾空白
替换方法
分割方法
val csv = "apple,banana,orange"
val lines = "line1\nline2\nline3"println(csv.split(",").toList) // List(apple, banana, orange)
println(lines.split("\n").toList) // List(line1, line2, line3)
println("a b c".split(" ")) // Array(a, b, c)
转换其他类型
同理,转换成其它类型的方法名也长得类似,此处不全部列出
集合操作方法(通过隐式转换)
数组
声明数组
设置值
多维数组
二维数组:val myMatrix = Array.ofDim[Int](3, 3)
定义了一个3*3
的二维数组
三维数组:val myMatrix = Array.ofDim[Int](3, 3, 3)
定义了一个3*3*3
的三维数组
同理可知多维数组的定义规则
常用方法
集合
Scala 集合分为可变的和不可变的集合。
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
List(列表)
列表是不可变的,值一旦被定义了就不能改变。List的特征是其元素以线性方式存储,集合中可以存放重复对象。
// 字符串列表
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))
Set(集合)
Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
Scala 集合分为可变的和不可变的集合。
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set
包。
注意:
虽然可变Set和不可变Set都有添加或删除元素的操作,但是有一个非常大的差别。对不可变Set进行操作,会产生一个新的set,原来的set并没有改变,这与List一样。
而对可变Set进行操作,改变的是该Set本身,与ListBuffer类似。
// 默认不可变Set
val set = Set(1,2,3)// 导入可变Set,并创建可变集合
import scala.collection.mutable
val mutableSet = mutable.Set(1,2,3)
Map(映射)
Map(映射)是一种可迭代的键值对(key/value)结构。
所有的值都可以通过键来获取。
Map 中的键都是唯一的。
默认情况下 Scala 使用不可变 Map。如果你需要使用可变集合,你需要显式的引入 import scala.collection.mutable.Map
类。在 Scala 中 你可以同时使用可变与不可变 Map,不可变的直接使用 Map,可变的使用 mutable.Map
。
// 空哈希表,键为字符串,值为整型
var A:Map[Char,Int] = Map()// Map 键值对演示
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")//定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号
A += ('I' -> 1)
A += ('J' -> 5)
Tuple(元组)
与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。
val t = (1, 3.14, "Fred")
//或
val t = new Tuple3(1, 3.14, "Fred")//访问元组的元素可以通过数字索引
println(s"第一个元素为 ${t._1}")
println(s"第二个元素为 ${t._2}")
Option(选项)
Option[T]
表示有可能包含值的容器,也可能不包含值。
Option[T]
是一个类型为 T
的可选值的容器: 如果值存在, Option[T]
就是一个 Some[T]
,如果不存在, Option[T]
就是对象 None
。
// 定义 Option
val x:Option[Int] = Some(5)
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
Iterator(迭代器)
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())}}
}
类和对象
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
Scala继承一个基类跟Java很相似, 但我们需要注意以下几点:
1、重写一个非抽象方法必须使用override修饰符。
2、只有主构造函数才可以往基类的构造函数里写参数。
3、在子类中重写超类的抽象方法时,你不需要使用override关键字。
继承会继承父类的所有属性和方法,Scala 只允许继承一个父类。
在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。
Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object
对象,它和类的区别是,object对象不能带参数。
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
Trait(特征)
Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。
Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,例如:
trait Equal {def isEqual(x: Any): Booleandef isNotEqual(x: Any): Boolean = !isEqual(x)
}
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。
构造器的执行顺序:
- 调用超类的构造器;
- 特征构造器在超类构造器之后、类构造器之前执行;
- 特征由左到右被构造;
- 每个特征当中,父特征先被构造;
- 如果多个特征共有一个父特征,父特征不会被重复构造
- 所有特征被构造完毕,子类被构造。
模式匹配
match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}
。
match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。
def matchTest(x: Any): Any = x match {case 1 => "one"case "two" => 2case y: Int => "scala.Int"case _ => "many"}
使用了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)
}
在声明样例类时,下面的过程自动发生了:
构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。
异常处理
Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
异常捕捉的机制与其他语言中一样,如果有异常发生,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")}}}
}
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...")}}
}
文件 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("菜鸟教程")writer.close()}
}
有时候我们需要接收用户在屏幕输入的指令来处理程序。实例如下:
import scala.io._
object Test {def main(args: Array[String]) {print("请输入菜鸟教程官网 : " )val line = StdIn.readLine()println("谢谢,你输入的是: " + line)}
}
从文件读取内容非常简单。我们可以使用 Scala 的 Source 类及伴生对象来读取文件。以下实例演示了从 “test.txt”(之前已创建过) 文件中读取内容:
import scala.io.Sourceobject Test {def main(args: Array[String]) {println("文件内容为:" )Source.fromFile("test.txt" ).foreach{ print }}
}
以上大部分内容整理自菜鸟教程🪂