以下内容基于对官方教程 SCALA BOOK 英文版的学习,文字和代码内容绝大多数来源于官方教程,顺序有所改变。与Scala学习笔记(一)重复的部分不再记载。
目录
字符串连接方式和字符串插值(string interpolation)
动态混入特质(Mixing traits in on the fly)
基础
特质App
一般的程序入口需要自己定义main方法,继承特质App后不用写main方法,类体代码会自动执行。
原因是特质App有main方法,并会把类体代码一一执行。App继承了DelayedInit,会延迟初始化。
object HelloYou extends App {
if (args.size == 0)
println("Hello, you")
else
println("Hello, " + args(0))
}
BigInt和BigDecimal
对于大数,Scala 还包括类型BigInt和BigDecimal
两种类型都支持直接使用算术运算符
字符串连接方式和字符串插值(string interpolation)
val name = firstName + " " + mi + " " + lastName
val name = s"$firstName ${mi} $lastName"
println(s"Name: ${firstName} ${mi} ${lastName}")
println(s"1+1 = ${1+1}")
// 双$转义输出一个$
println(s"New offers starting at $$14.99")
// 加f可以到达类似printf格式化输出的目的
val height = 1.9d
val name = "James"
println(f"$name%s is $height%2.2f meters tall") // James is 1.90 meters tall
// 加raw不会进行转义
println(raw"a\nb") // a\nb
// 双引号需要转义时可以包含在三个双引号之间
val person = """{"name":"James"}""" // 输出内容:{"name":"James"}
// 多行字符串包含在三个双引号之间
// 从第二行开始缩进空格也会包含在字符串内容中
val speech = """Four score and
seven years ago
our fathers ..."""
// 从第二行开始文字前放|,最后加stripMargin会保证左对齐
val speech = """Four score and
|seven years ago
|our fathers ...""".stripMargin
// 可以自定义字符串插值,例如
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
def json(args: Any*): JSONObject = {
val strings = sc.parts.iterator
val expressions = args.iterator
var buf = new StringBuilder(strings.next())
while(strings.hasNext) {
buf.append(expressions.next())
buf.append(strings.next())
}
parseJson(buf)
}
}
/* 当使用 json"{ name: $name, id: $id }"时,编译器会重写为
new StringContext("{ name: ", ", id: ", " }").json(name, id)
然后会利用隐式类再次重写为
new JsonHelper(new StringContext("{ name: ", ", id: ", " }")).json(name, id)
*/
命令行IO
println("Hello, world")
print("Hello without newline")
System.err.println("yikes, an error happened")
val firstName = readLine()
流程控制
if/else
if/else结构和java类似
与java不同的是可以当作三目运算符给变量赋值
if (a == b) doSomething()
if (test1) {
doX()
} else if (test2) {
doY()
} else {
doZ()
}
val minValue = if (a < b) a else b
for循环
val nums = Seq(1,2,3)
for (n <- nums) println(n)
nums.foreach(println)
// Map使用for和foreach
val ratings = Map(
"Lady in the Water" -> 3.0,
"Snakes on a Plane" -> 4.0,
"You, Me and Dupree" -> 3.5
)
for ((name,rating) <- ratings) println(s"Movie: $name, Rating: $rating")
ratings.foreach {
case(movie, rating) => println(s"key: $movie, value: $rating")
}
// yield关键字可以利用每次循环的返回值生成一个新的集合
val names = List("_adam", "_david", "_frank")
val capNames = for (name <- names) yield name.drop(1).capitalize
println(capNames) // List(Adam, David, Frank)
match模式匹配
更多内容参照Scala学习(一)的模式匹配描述
val evenOrOdd = i match {
case 1 | 3 | 5 | 7 | 9 => println("odd")
case 2 | 4 | 6 | 8 | 10 => println("even")
case _ => println("some other number")
}
i match {
case a if 0 to 9 contains a => println("0-9 range: " + a)
case b if 10 to 19 contains b => println("10-19 range: " + b)
case c if 20 to 29 contains c => println("20-29 range: " + c)
case _ => println("Hmmm...")
}
异常处理
try {
// your scala code here
}
catch {
case foo: FooException => handleFooException(foo)
case bar: BarException => handleBarException(bar)
case _: Throwable => println("Got some other kind of Throwable exception")
} finally {
// your scala code here, such as closing a database connection
// or file handle
}
Option/Some/None,正常时返回Some,异常时返回None,后续处理进行判断
def toInt(s: String): Option[Int] = {
try {
Some(Integer.parseInt(s.trim))
} catch {
case e: Exception => None
}
}
toInt(x) match {
case Some(i) => println(i)
case None => println("That didn't work.")
}
// 正常时返回Some(a+b+c),异常时返回None
val y = for {
a <- toInt(stringA)
b <- toInt(stringB)
c <- toInt(stringC)
} yield a + b + c
Try/Success/Failure,正常时返回Success,异常时返回Failure,后续处理进行判断
def toInt(s: String): Try[Int] = Try {
Integer.parseInt(s.trim)
}
toInt(x) match {
case Success(i) => println(i)
case Failure(s) => println(s"Failed. Reason: $s")
}
// 正常时返回Success(a+b+c),异常时返回Failure
val y = for {
a <- toInt(stringA)
b <- toInt(stringB)
c <- toInt(stringC)
} yield a + b + c
案例对象(case objects)
同时拥有案例类(case classes)和单例对象的特性,但是不能有参数,一般用来做枚举类型
sealed trait Topping
case object Cheese extends Topping
case object Pepperoni extends Topping
case object Sausage extends Topping
case object Mushrooms extends Topping
case object Onions extends Topping
sealed trait CrustSize
case object SmallCrustSize extends CrustSize
case object MediumCrustSize extends CrustSize
case object LargeCrustSize extends CrustSize
sealed trait CrustType
case object RegularCrustType extends CrustType
case object ThinCrustType extends CrustType
case object ThickCrustType extends CrustType
case class Pizza (
crustSize: CrustSize,
crustType: CrustType,
toppings: Seq[Topping]
)
动态混入特质(Mixing traits in on the fly)
trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}
trait Runner {
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")
}
class Dog(name: String)
val d = new Dog("Fido") with TailWagger with Runner
d.startTail // tail is wagging
d.startRunning // I'm running
抽象类
Scala 也有一个抽象类的概念,类似于 Java 的抽象类。
多数抽象类的功能都可以使用特质(trait)来实现,有下面要求的时候就得使用抽象类
创建一个需要构造函数参数的基类
Scala代码需要从Java代码中调用
abstract class Pet (name: String) {
def speak(): Unit = println("Yo") // concrete implementation
def comeToMaster(): Unit // abstract method
}
class Dog(name: String) extends Pet(name) {
override def speak() = println("Woof")
def comeToMaster() = println("Here I come!")
}
Scala集合
ArrayBuffer
索引的、可变的序列,类似java的ArrayList
import scala.collection.mutable.ArrayBuffer
val ints = ArrayBuffer[Int]()
val nums = ArrayBuffer(1, 2, 3)
// add one element
nums += 4
// add multiple elements
nums += 5 += 6
// add multiple elements from another collection
nums ++= List(7, 8, 9)
// remove one element
nums -= 9
// remove multiple elements
nums -= 7 -= 8
// remove multiple elements using another collection
nums --= Array(5, 6)
val a = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3)
a.append(4) // ArrayBuffer(1, 2, 3, 4)
a.append(5, 6) // ArrayBuffer(1, 2, 3, 4, 5, 6)
a.appendAll(Seq(7,8)) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
a.clear // ArrayBuffer()
val a = ArrayBuffer(9, 10) // ArrayBuffer(9, 10)
a.insert(0, 8) // ArrayBuffer(8, 9, 10)
a.insertAll(0, Vector(4, 5, 6, 7)) // ArrayBuffer(4, 5, 6, 7, 8, 9, 10)
a.prepend(3) // ArrayBuffer(3, 4, 5, 6, 7, 8, 9, 10)
a.prepend(1, 2) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
a.prependAll(Array(0)) // ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g)
a.remove(0) // ArrayBuffer(b, c, d, e, f, g)
a.remove(2, 3) // ArrayBuffer(b, c, g)
val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g)
a.trimStart(2) // ArrayBuffer(c, d, e, f, g)
a.trimEnd(2) // ArrayBuffer(c, d, e)
List
线性的、不可变的序列。一个无法修改的链表。
因为List是一个链表类,最好不要通过索引值访问大列表的元素。
val ints = List(1, 2, 3)
val names = List("Joel", "Chris", "Ed")
val a = List(1,2,3)
val b = 0 +: a // 生成新的list,操作符引号在列表的一侧
val c = a :+ 4 // 生成新的list,操作符引号在列表的一侧
val d = List(-1, 0) ++: a // 生成新的list
val list = 1 :: 2 :: 3 :: Nil // List(1, 2, 3)
Vector
索引的、不可变的序列。
使用方法和List一样,唯一区别是用下标访问比List快
Map
由键和值组成的一个可迭代序列。
Scala 有可变Map类和不可变Map类。
import scala.collection.mutable.Map
val states = Map("AK" -> "Alaska")
// add one element
states += ("AL" -> "Alabama")
// add multiple elements
states += ("AR" -> "Arkansas", "AZ" -> "Arizona")
// add multiple elements from another collection
states ++= Map("CA" -> "California", "CO" -> "Colorado"))
// remove one element
states -= "AR"
// remove multiple elements
states -= ("AL", "AZ")
// remove multiple elements using another collection
states --= List("AL", "AZ")
// update
states("AK") = "Alaska, A Really Big State"
// iterate over Map elements
for ((k,v) <- states) printf("key: %s, value: %s\n", k, v)
// get the keys from a Map
val keys = states.keys
// get the values from a Map
val values = states.values
// test if a Map contains a key
val contains3 = states.contains("AK")
// transform Map values
val ucMap = states.transform((k,v) => v.toUpperCase)
// filter a Map by its keys
val twoAndThree = states.view.filterKeys(Set(2,3)).toMap
// take the first two elements from a Map,仅对排序Map有意义
val firstTwoElements = states.take(2)
// filter elements by supplying a function that operates on the keys and/or values
states.filterInPlace((k,v) => k == "AK")
Set
可迭代的集合,没有重复的元素。
import scala.collection.mutable.Set
val set = Set[Int]()
set += 1
set += 2 += 3
set ++= Vector(4, 5)
// 尝试向已在其中的集合添加值,将被忽略
set += 2
// 会返回是否添加到集合中
set.add(6)
set -= 1
set -= (2, 3)
set --= Array(4,5)
// 会返回是否删除了元素
set.remove(2)
set.clear()
集合序列的常用方法
下面的方法是集合序列类通用的
map,filter,take,takeWhile,drop,dropWhile
不会改变集合,返回一个带有修改结果的新集合。
head,tail,reduce
返回一个值
val ints = List.range(1, 10)
// 生成一个新的列表,每个值为原来的两倍
val doubledInts = ints.map(_ * 2)
// 大于5的值生成一个新的列表
val x = ints.filter(_ > 5)
// 偶数值生成一个新的列表
val x = ints.filter(_ % 2 == 0)
// 循环打印
ints.foreach(println)
// 返回第一个元素
ints.head
// 返回f
"foo".head
// 返回最后一个元素
ints.tail
// 返回r
"bar".tail
// 拿出前两个元素生成一个新的集合
ints.take(2)
// 取出从第一个开始满足条件的元素,直到遇到不满足条件的元素
ints.takeWhile(_ < 5)
// 扔掉前5个元素,剩下的生成一个新的集合
ints.drop(5)
// 扔掉从第一个开始满足条件的元素,直到遇到不满足条件的元素,剩下的生成一个新的集合
ints.dropWhile(_ < 5)
// 把集合中所有元素相加,最后得到一个单一值
ints.reduce(_ + _)