Scala简洁入门小结
介绍
Scala是一门现代的多范式语言,志在以简洁、优雅及类型安全的方式来表达常用的编程模型。它平滑地集成了面向对象和函数式语言的特性。
推荐阅读:Scala 2 的官方中文文档
本文可以分为三部分:
- 基本语法
- Scala面向对象
- Scala容器
基本语法
速览
首先来看一段简单的 Scala 程序,如果有Java 以及 Python 基础的话,那么这一段代码将会非常容易理解:
// 使用 object 定义一个单实例,类似于 class 关键字,但是有区别,后文中会提到
object example_summary {
// 定义了一个方法,这个方法需要两个参数 x 以及 y,两个参数都是 Int 类型
// 方法的返回值也是 Int 类型对象
def multiply(x:Int,y:Int):Int={
// 方法体中的最后一个表达式就是方法的返回值
x * y
// Scala 中也有一个 return 关键字,但是很少用
// return x * y
}
// 使用 class 关键字创造一个普通类,后面跟着它的名字和构造参数
// 主构造函数,也可以自定义辅助构造函数,后文中会提到
class Greeter(prefix:String, suffix:String){
// 方法 out 返回类型是 Unit,类似于 void
def out(name:String): Unit ={
println(prefix + name + suffix)
}
}
// Main 函数,程序的入口
// 类似于 Java 的 Main 函数需要一个 String[] args
// Scala 也需要一个字符串列表 Array 是数组关键字
def main(args:Array[String]):Unit={
val x: Int = 1+4; // 定义 x 为整形类型,val 是常量,赋值之后不能在变化
var s: String = "first val"; // var 是变量,赋值之后可以再变化
var t = "first var"; // Scala 具有类型推断的能力,故而可以不指定类型来声明
println(s) // first val
println(t) // first var
println(s.indexOf('v')) // 6
println({ // 代码块中最后一个表达式的结果是代码块的计算结果
val x = 1 + 1 // 代码块中定义的变量属于局部变量
x + 1
}) // 3
println(x) // 5
println(multiply(11,11)); // 121 可以使用 ; 来结束一行,但是没必要
var greeter = new Greeter("欢迎 ", " 光临")
greeter.out("黄伟"); // 欢迎 黄伟 光临
}
}
类型
在 Scala 中,所有的值都有类型。
- Any 是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如
equals、hashCode和toString。 - AnyVal 代表值类型。有9个预定义的非空的值类型分别是:
Double、Float、Long、Int、Short、Byte、Char、Unit和Boolean。 - AnyRef 代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是
AnyRef的子类型。
类型转换
值类型可以按照下面的方向进行转换:
// 允许的转换方向
val x: Long = 987654321
val y: Float = x // 9.8765434E8 (note that some precision is lost in this case)
val face: Char = '☺'
val number: Int = face // 9786
// 不允许的转换方向
val x: Long = 987654321
val y: Float = x // 9.8765434E8
val z: Long = y // Does not conform
Scala循环
Scala 提供了三种循环方法,分别是 while 循环, do-while 循环以及 for 循环:
object test {
def main(args:Array[String]): Unit ={
// while 循环
var a = 0
while(a<10){
print(a)
a+=1
}
// do-while 循环
var b = 0
do{
print(b)
b+=1
}while(b<10)
// for 循环,从 0 到 10,右为开区间 until
for(c <- 0 until 10){
print(c)
}
// for 循环,从 0 到 10,右为闭区间 to
for(d<-0 to 10){
print(d)
}
// Scala 的 break 语法,有点特殊
val loopBreaker = new Breaks;
loopBreaker.breakable{
for(e <- 0 to 10){
print(e)
loopBreaker.break()
}
}
}
}
Scala异常处理
Scala 的异常处理和其它语言比如 Java 类似。Scala 抛出异常的方法和 Java一样,使用 throw 方法:
throw new RuntimeException()
Scala中的异常处理借鉴了模式匹配的思想:
object test {
def main(args:Array[String]): Unit ={
try {
// 读取文件
val f = new FileReader("input.txt")
} catch {
// 捕获异常 FileNotFoundException
case ex: FileNotFoundException =>{
println("Missing file exception")
}
// 捕获异常 IOException
case ex: IOException => {
println("IO Exception")
}
// 捕获异常 RuntimeException
case ex: RuntimeException=>{
println("RuntimeException")
}
}finally {
println("Finish!")
}
}
}
Scala访问修饰符
Scala的访问修饰符有三种,分别是:private,protected 以及 public。在默认情况下,Scala的访问级别都是 public。
和Java 略有区别的是:
private不允许外部类访问内部类的私有成员protected不允许同一个包下非子类访问
作用域保护
Scala中,访问修饰符可以通过使用限定词强调,格式为:
private[x]
protected[x]
这里的x指代某个所属的包、类或单例对象。如 private[x] 表示这个成员除了对x中的类、包中类或者伴生对象可见外,对其他所有类都是 private。
Scala面向对象
类
下面是一个定义类的例子:
// 使用 class 来定义 Person 类
// 拥有一个主构造函数,参数为 name, age 以及 company
// 构造函数中的参数如果使用 var 或者 val 修饰,则将会是类的成员变量
// 否则只能被当做一个类内不可变参数使用,不能被当做类的字段,外部也不能访问该变量
class Person(var name:String, var age:Int, var company:String){
// 定义一个成员变量 hobby 类型为字符串
var hobby:String = ""
// 定义一个辅助构造函数,辅助构造函数中的第一个语句必须是调用主构造函数
def this(name:String, age:Int)={
this(name,age,"")
}
// 定义一个成员方法,接收一个字符串参数 newHobby,将成员变量 hobby 设置为 newHobby
def addHobby(newHobby:String): Unit ={
this.hobby = newHobby
}
// 重写父类 Any 的 toString方法
override def toString(): String = {
var result = "Person " + "Name: " + name
if(company.isEmpty)result = result + " Unknown company"
else result = result + " Company: " + company
if(this.hobby.nonEmpty)result = result + " Hobby: " + this.hobby
result
}
}
object test {
def main(args:Array[String]): Unit ={
val hw = new Person("黄伟", 23, "XiaoMi")
val p = new Person("小明",22)
println(hw) // Person Name: 黄伟 Company: XiaoMi
println(p) // Person Name: 小明 Unknown company
hw.addHobby("看电影")
println(hw) // Person Name: 黄伟 Company: XiaoMi Hobby: 看电影
}
}
私有成员
类中的成员默认是 public 的,可以使用 private 进行修饰:
class Person(name:String, var age:Int, var company:String){
// 定义了私有变量 address_
private var address_ = "Unknown"
// 定义了私有变量 address_ 的 Getter 方法
def address: String = address_
// 定义了私有变量 address_ 的 Setter 方法
def address_=(newAddress:String):Unit={
this.address_ = newAddress
}
// 定义了私有方法 getAddress
private def getAddress:String={
s"Live in $address_"
}
override def toString: String = {
s"Person Name: $name, Age: $age Company: $company Address: $getAddress"
}
}
object test_map {
def main(args:Array[String]): Unit ={
val hw = new Person("黄伟", 23, "XiaoMi")
val p = new Person("小明",25, "Xiaomi")
hw.address="南京市建业区"
println(hw)
println(p)
}
}
接口/特质
在Scala中使用特质 Trait 关键字来定义接口,使用 extends 来拓展接口,使用 override 来实现接口中定义的方法,使用关键字 with 来多继承接口:
// scala 中的特质,可以理解为接口
trait Drink{
def drink(name:String): Unit
}
// scala 中的特质(接口)可以定义属性,也可以定义默认实现
trait Eat{
def eat(name:String):Unit={
println(s"I want to eat $name")
}
}
// 第一个接口使用 extends,后续的接口使用关键字 with
class Person(name:String) extends Drink with Eat{
override def drink(drinks: String): Unit = {
println("the person " + name + " want to drink " + drinks)
}
override def eat(food:String):Unit={
println("the person " + name + " want to eat " + food)
}
}
object example {
def main(args:Array[String]):Unit={
val p = new Person("小明")
p.drink("乌龙茶") // the person 小明 want to drink 乌龙茶
p.eat("炒饭") // the person 小明 want to eat 炒饭
}
}
抽象类与混合继承
Scala 如同 Java 一般也拥有抽象类,可以实现多个接口但是只能有一个父类:
// 抽象类
abstract class A {
val message: String
}
// 实现了抽象类,并且改写了 message 属性
class B extends A {
val message = "I'm an instance of class B"
}
// 接口拓展了抽象类 A ,并且增加了一个方法
trait C extends A {
def loudMessage(): String = message.toUpperCase()
}
// 类 D 继承了类 B 和接口 C
class D(val name:String) extends B with C{
println(s"Create a instance $name of class D")
}
object example_mixIn {
def main(args:Array[String]): Unit ={
val d = new D("Data") // Create a instance Data of class D
println(d.loudMessage()) // I'M AN INSTANCE OF CLASS B
}
}
Scala容器
Scala数组
// 定义一个长度为 3 的数组,元素类型为字符串
var z = new Array[String](3)
// 也可以直接使用这种方式初始化一个数组
var z = Array("aaa", "bbb", "ccc")
循环遍历数组:
object test {
def main(args:Array[String]): Unit ={
var array = new Array[Int](10)
for(i<-0 until 10)
array(i) = i
for(i<-0 until 10)
println(array(i))
}
}
函数式编程
函数式编程常用的几个方法是 filter,sortBy,map以及foreach:
object test {
def main(args:Array[String]): Unit ={
var array = Array("test", "Xiaomi", "xiaoming", "aaa", "ccc")
array
.filter(item => item.length > 3)
.sortBy(item => item.length)
.map(item => item + " Length: " + item.length)
.foreach(println)
// test Length: 4
// Xiaomi Length: 6
// xiaoming Length: 8
}
}
Scala集合-列表
object test_map {
def main(args:Array[String]): Unit ={
val list = List("111", "222", "test", "XiaoMi")
val temp = List("other", "new")
println(list(1)) // 按照索引获取列表元素 222
println(list.tail) // 获取除第一个元素之外的列表 List(222, test, XiaoMi)
println(list.head) // 获取列表中首个元素 111
println(list.concat(temp)) // 拼接两个列表
println(List.fill(5)(0)) // 通过填充新建一个长度为 5 元素为 0 的数组
println(list.mkString(" = ")) // 111 = 222 = test = XiaoMi
}
}
Scala集合-Set
object test_map {
def main(args:Array[String]): Unit ={
var set1 = Set(1,2,3)
val set2 = Set(4,6,7)
set1 = set1 + 4
println(set1.exists(_ % 2 == 0)) //true 判断集合 set1 中是否存在偶数
println(set1.drop(1)) //Set(2,3,4)
println(set1 & set2) // 求集合的交集
println(set1 &~ set2) // 求集合的差集
}
}
Scala集合-Map
object test {
def main(args:Array[String]): Unit ={
var a = Map("1"->"test", "2"->"xiaomi", "3"->"xiaoming", "4"->"good")
a.foreachEntry((key, value) => println("Key: " + key + " Value: " + value))
// 在字典中新增一个键值对
a += ("5" -> "better")
// 在字典中删除一个键
a -= "1"
a.keys
.filter(item => a(item).length > 2)
.map(item => "key: " + item + " Value: "+ a(item))
.foreach(println)
}
}
Scala中的Option
在Scala中,使用Option对象表示可能为空的对象,如果返回不为空,则返回Some子类,如果返回为空,则返回None子类。
object test {
def matchOption(x:Option[String]):String={
x match{
case Some(s) => s
case None => "invalid"
}
}
def main(args:Array[String]): Unit ={
var map = Map('a'->"aaa", 'b'->"bbb", 'c'->"ccc")
var item = map.get('a')
println(item) // Some(aaa)
println(item.get) // aaa
println(map.get('d')) // None
println(matchOption(item)) // aaa
println(matchOption(map.get('d'))) // invalid
}
}