scala不刻意区分原生类型和引用类型
scala单参数方法可以用运算符形式调用,例如:
// 这两种写法等效
a.add(b)
a add b
scala没有提供"++" 和 "--" 运算符,可以使用:
a += 1
a -= 1
import scala开头的包可以省略scala前缀
// 以下写法等效
import scala.collection.mutable.Map
import collection.mutable.Map
apply方法可以直接在类名或者实例变量名后直接用小括号来调用
class Demo {
def apply(n: int): Unit = {}
}
可以这么调用
val demo = new Demo
demo(6) //相当于demo.apply(6)
object Demo {
def apply(n: int): Unit = {}
}
可以这么调用
Demo(6)
这种写法多用于构建对象,例如:
object Demo {
def apply(n: Int) = {
val demo = new Demo
demo.setXXX
// ...
demo
}
}
class Demo {
// ...
}
scala没有checked exception
scala中if 和 else是一个表达式,因此可以这么使用:
val a = if (1 > 0) 1 else 0
如果这样写:
val a = if (1 < 0) 1
则相当于:
val a = if (1 < 0) 1 else ()
()是一个Unit类,代表无值
{}代码块包含一系列表达式,代码块的值就是最后一个表达式的值
val s = {
val a, b = 1
a + b
}
// s == 2
代码块如果最后一条语句为赋值语句,则代码块的值为Unit,例如:
{
var n = 0
n += 1
}
readLine函数从控制台读取输入:
val str = readLine("Please input a string")
for循环:
for (i <- 0 to 10) {}
// i 从0到10,包含0和10
for(i <- 0 until 10) {}
// i 从0到10,包含0不包含10
// 取字符串中每一个char可以使用循环:
for(s <- "Hello") {}
scala for循环没有break和continue,如果想break可以使用:
import scala.util.control.Breaks._
breakable {
for (s <- 1 to 10) {
if (...) break
}
}
高级for循环:
for (i <- 1 to 3; j <- 1 to 3) println(i *10 + j)
推导式中可以带守卫:
for (i <- 1 to 3; j <- 1 to 3 if i != j) println(i *10 + j)
可以在for循环中定义变量:
for (i <- 1 to 3; from = 4 - i; j <- from to 3) println(i *10 + j)
for循环的循环体以yield开始可以构造出一个新的集合:
val collection = for (i <- 1 to 3; from = 4 - i; j <- from to 3) yield i *10 + j
函数无需指明返回类型,但是递归函数必须指明
函数参数可以指定默认值:
def add (x: int, y: int = 1) = {x + y}
add(1) // 返回2
add(1,3) // 返回4
变长参数:
def add (x: int, y: int*) {
// y被认为是数组
for(i <- y) {}
}
集合转换成变长序列:
val a = 1 to 5
add(1, a: _*)
懒值:懒值介于val和def之间
val result = ... // 被定义的时候执行
lazy val result = ... // 第一次使用的时候执行
def result = ... // 每一次使用的时候都执行
throw表达式的类型为Nothing。if/else中如果一个分支的类型为Nothing则表达式的类型为另一个分支的类型:
if (x >=0) {sqrt(x)} else throw new IllegalArgumentException("...") //类型为Double
异常捕获采用的是模式匹配:
try {
...
} catch {
case _: ExceptionA => print("...")
case e: ExceptionB => e.printStackTrace()
}
定长数组时Array,变长数组为ArrayBuffer
定义数组:
val arr = new Array[Int](10) // 定义数组arr,长度为10,类型为Int,所有值初始化为0
val stringArr = Array("Hello", "World")
val s = stringArr(0) // 值为"Hello"
ArrayBuffer运算:
val a = new ArrayBuffer[int]()
a += 1// a为 (1)
a += (2, 3, 4, 5) // a为 (1, 2, 3, 4, 5)
a ++= Array(6, 7, 8, 9, 10) // a为(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
a.trimEnd(5) // a为 (1, 2, 3, 4, 5)
b.insert(index, 被插入元素序列)
b.remove(起始index, 移除元素个数)
ArrayBuffer 转换为 Array
arr.toArray
遍历数组
for(i <- 0 until a.length) {} // 使用index
for(e <- a) {} // 不使用index
0 until (a.length, 2) // step为2
(0 until a.length).reverse // 逆序
数组常用方法:
- sum
- max
- sorted
- scala.util.Sorting.quickSort
- mkString
多维数组:
val a = Array.ofDim[Double](3, 4) // 3行4列
a(4)(2)
构造Map:
val map = Map("key1" -> 1, "key2" -> 2) // 不可变map
val map = scala.collection.mutable.Map("key1" -> 1, "key2" -> 2) // 可变map
映射是对偶的集合,"key1" -> 1 构造了一个对偶,相当于("key1", 1),因此map可以这样构造:
val map = Map(("key1", 1), ("key2", 2))
获取map的值:
val value = map("key1")
map是否存在key:
map.contains("key")
map.getOrElse("key", 0) // key存在则返回key对应的值,否则返回0
map.get("key") // 返回Option对象,要么是Some(值),要么是None
更新可变映射:
map("key") = 1
map += ("key3" -> 3, "key4" -> 4)
map -= "key4" // 移除key为"key4"的entry
遍历映射:
for((key, value) <- map) {}
for((key, value) <- map) yield (value, key) // 反转映射
map.keySet // 返回键的集合
map.values // 返回值的集合
元组
val tuple = (1, "Paul")
val name = tuple._1 // 返回1,下标从1开始而不是0
val (id, name) = tuple // id为1,name为Paul
val (id, _) = tuple // id 为1
patition分区操作:
"ABCdef".patition(_.isUpper) // 返回("ABC", "def")
zip操作:
val a = Array("a", "b", "c")
val b = Array(1, 2, 3)
val c = a.zip(b) // c为Array(("a", 1), ("b", 2), ("c", 3))
对偶的集合可以使用toMap转换为map
方法的调用:
class A {
def func() = {}
}
可以使用a.func()
或者a.func
方式调用,但是定义时候如果没有给出小括号,例如:
class A {
def func = {}
}
则只能使用a.func
方式调用
如果成员变量的名字为a,则在scalar中a的getter方法名为a
,setter方法名为a_=
var
变量会自动生成setter和getter
如果变量是private的则setter和getter都是private的
val
声明的变量只有getter方法
private[this]
变量没有setter和getter方法
private
和private[this]
的区别
class Counter {
private var count = 0
def isLess(other: Counter) = count < other.count
// 虽然count是private类型变量,但是other是Counter类型,因此在 Counter内部能够正常访问
// 此处counter若为private[this],Counter类只能访问当前对象的count变量
}
自动生成java bean的setter和getter方法
@BeanProperty var a = 0
主构造器:
class Student(val name: String, val age: Int)
主构造器会执行类定义中的所有语句
如果声明构造器参数没有指明val或者var,则生成的字段为private[this]。若该字段未被使用则不会生成该字段。
私有化主构造器:
class Person private (val name: String)
类型投影:
ArrayBuffer[Network#Member] // 任意Network实例中的Member对象
单例对象:
object Person {
}
```scala
main函数:
```scala
object Demo {
def main(args: Array[String]) {}
}
也可以扩展App特质:
object Demo extends App {
// main函数执行的内容
}
枚举类型
object Direction extend Enumeration {
type Direction = Value
val EAST, WEST, SOUTH, NORTH = Value
}
包可以写成嵌套格式:
package com {
package paul {
class A
}
}
scala中的包名是相对的,如果要使用绝对包名,需要以root开始
包对象:
每个包都有一个包对象,需要在父包中定义它,名字和子包一样。
package object people {}
引入包的全部成员:
import com.paul._
scala中import语句可以出现在任何地方,不局限于文件顶部
包选取器,用于引入包中的几个成员:
import java.awt.{Color, Font}
import java.util.{HashMap => JavaHashMap} //使用别名
重写方法必须使用override修饰符
isInstanceOf() // 检查类型
asInstanceOf() // 转换类型
如果只想测试p是不是某一个特定的类而不是其子类的话,可以:
if (p.getClass == classOf[A]) {}
scala中protected字段对于同一个包的其他类而言是不可见的,只有它的子类可见。这点和Java是不同的
超类构造器:
class Person(val name: String)
class User(val name: String, val password: String) extends Person(name)
def只能重写def
val只能重写另一个val或不带参数的def
var只能重写另一个抽象的var
提前定义:
class Ant extends {
override val range = 2
} with Creature
重写变量range会在Creature构造函数之前执行
scala检查对象是否相等可以用==运算符
读取文件行:
import scala.io.Source
val source = Source.fromFile("myFile.txt", "UTF-8")
val lineIterator = source.getLines
for (line <- lineIterator) {}
source.close()
for (c <- source) {} // 处理单个字符
val iter = source.buffered
iter.head // peek下一个字符,但不取出
从URL或者是其他源读取数据:
val source1 = Source.fromURL()
val source1 = Source.fromString("Hello World")
val source3 = Source.fromStdin()
读取二进制文件需要使用Java的类库
Scala序列化:
@SerialVersionUID(42L) class Person extends Serialization
如果使用默认的SerialVersionUID可以省略该注解
scala执行系统命令:
import system.process._
"ls -al .." ! // 执行结果被打印到标准输出
!返回结果是被执行程序的返回值,如果正常返回0,否则显示非0值
val result = "ls -al .." !! // 输出结果会以字符串的形式返回
"ls -al .." #| "grep sec" ! // #| 为管道操作符
"ls -al .." #> new File("file.txt") ! // 输出重定向到文件
"ls -al .." #>> new File("file.txt") ! // 输出追加到文件末尾
"grep sec" #< new File("output.txt") ! // 文件内容作为输入
"grep sec" #< new File("output.txt") ! // URL内容作为输入
p #&& q // 如果p成功,执行q
p #|| q // 如果p不成功,执行q
正则表达式:
val regexp = """[abc]{1,3}""".r // String的r方法生成正则表达式。为了避免转义,可以使用原始字符串语法:"""..."""
迭代匹配值:
for (matching <- regexp.findAllIn("...")) {}
匹配值转换为数组:
val matches = regexp.findAllIn("...").toArray
其他正则表达式方法:
findfirstIn, findPrefixOf, replaceFirstIn, replaceAllIn
正则表达式组:
val numItemPattern = """(\d+) ([A-Za-z]+)""".r
匹配组:
val numItemPattern (num, item) = "99 bottles"
匹配多个组
for (numItemPattern (num, item) <- numItemPatter.findAllIn("99 bottles, 100 kettles"))
实现多个特质:
class A extend B with C with D
如果特质中方法已经实现,子类中重写该方法必须要添加override关键字。如果特质中方法未实现,则写不写override均可
class A extends B with C {
def f = {
super.func()
// 如果特质B和C中都有方法func,则按照从左到右的优先循序,执行B中的func方法
super[C].func()
// 明确指明调用特质C中的func方法
}
}
特质中出现的具体字段会被添加到子类中
trait Parent {
val count = 0
}
class Child extends Parent {
// 相当于也加入了val count = 0
}
特质不能有构造器参数,每个特质都有一个无参数构造器。
特质可以扩展类,该类会自动成为混入该特质的类的超类
自身类型:
特质可以以如下代码开始:
this: 类型A =>
...
如此定义的特质只能被混入类型A的子类当中
自身类型也可以处理结构类型:
trait A {
this: {def getMessage(): Unit} =>
...
// 只有具有getMessage方法的类才能混入该特质
}
yield事scala的保留字,如果调用java的方法中包含该单词,需要反引号。例如:Thread.yield
定义前置操作符:
class A {
def unary_-(): Unit = {}
}
调用该前置操作符时可使用:-a,相当于a.unary_-
用于构造列表的::操作符是右结合的:
1 :: 2 :: Nil
的意思是1 :: (2 :: Nil)
2 :: Nil
相当于Nil.::(2)
函数调用语法可以扩展到函数之外的值,例如:
f(x, y, z)
相当于调用f.apply(x, y, z)
f(x, y, z) = value
相当于调用f.update(x, y, z, value)
提取器:
class Fraction(val num: Int, val den: Int)
object Fraction {
def unapply(input: Fraction): Option[(Int, Int)] = {
if (input.den == 0) None else Some((input.num, input.den))
}
}
val Fraction(a, b) = new Fraction(3, 4) // a 为 3, b 为 4
// unapply方法用在赋值语句和模式匹配中
unapplySeq方法:匹配任意长度序列
将函数本身赋予一个变量:
import math._
val fun = ceil _
匿名函数:
(x: Double) => 3 * x
函数参数可以放在大括号中:
Array(1, 2, 3).map((x: Int) => 3 * x)
Array(1, 2, 3).map{(x: Int) => 3 * x}
函数接收函数为参数:
def f(g: (Int) => Int) = g(10)
f((x: Int) => 3 * x)
x的类型可推断为Int,因此类型可以省略为:
f((x) => 3 * x)
参数列表中只有一个参数,小括号可以省略:
f(x => 3 * x)
参数在=> 右侧仅出现了一次,可以省略为:
f(3 * _) // 简写方式仅能在参数类型已知的情况下有效
函数柯里化:
def mulOneAtATime(x: Int)(y: Int) = x * y
mulOneAtATime(3)(4)
柯里化的用途之一:提供更多的类型推断信息
val a = Array("Hello", "World")
val b = Array("hello", "world")
a.corresponds(b)(_.equalsIgnoreCase(_))
corresponds的定义为:
def corresponds[B](that: Seq[B])(p: (A: B) => Boolean):Boolean
可以根据第一个参数传进去的类型B,以用于第二个参数p
def runInThread(block: () => Unit) = {
new Thread {
override def run() {block()}
}.start()
}
runInThread{() => println("")}
可以将参数列表省略掉:
def runInThread(block: => Unit) = {
new Thread {
override def run() {block} // 调用时不需要加括号
}.start()
}
runInThread{println("")} // 传递匿名函数时不需要参数列表
不可变序列:
Indexed: Vector, Range
non-indexed: List, Stream, Stack, Queue
可变序列:
indexed: ArrayBuffer
non-indexed: Stack, Queue, PriorityQueue, LinkedList, DoubleLinkedList
List包含head和tail
List(9, 4, 2) 相当于 9 :: 4 :: 2 :: Nil
集合应用的view方法返回的是懒视图,只对被求值的时候进行计算
可以使用force方法对懒集合进行强制计算
使用线程安全的集合:混入Synchronized开头的几个集合特质,例如:
val synchronizedMap = new scala.collection.mutable.HashMap[String, Int] with scala.collection.mutable.SynchronizedMap[String, Int]
集合并行计算:调用par函数
for(i <- (0 until 100).par) print(i + " ")
for(i <- (0 until 100).par) yield i + " "
并行集合转换为串行集合:调用ser方法
模式匹配:
p match {
case '+' => ...
case _: Int => ...
case e: Exception => e.printStackTrace()
case _: Int if p % 2 == 0 => ...
case _ => ... // 相当于default
}
match语句也可以是表达式
for中也可以使用模式:
for((key, "") <- System.getProperties()) {// key为所有值为空白的键}
样例类case class:
样例类构造器每一个参数自动称为val,除非被声明为var
使用伴生对象的apply方法创建新对象
提供unapply方法,可使用模式匹配
生成toString, hasCode, equals和copy方法
copy方法的使用:
case class Person(name: String, age: Int)
val p1 = Person("Paul", 20)
val p2 = p1.copy() // p2为 name="Paul" age=20
val p3 = p1.copy(age=30) // p3 为 name="Paul" age=30
嵌套值绑定到变量:使用@符号
密封类:
使用样例类做模式匹配是,编译器确保已经列出了所有可能的选择
密封类所有的子类都必须在密封类所在的文件中定义
sealed abstract class Direction
case object EAST extends Direction
case object WEST extends Direction
case object NORTH extends Direction
case object SOUTH extends Direction
d match {
case EAST => ...
case WEST => ...
case NORTH => ...
case SOUTH => ...
}
偏函数:被花括号包围的一组case语句为偏函数。
val f: PartialFunction[Char, Int] = {case '+' => 1, case '-' => -1}
f('-') // 调用f.apply('-') 返回 -1
f.isDefinedAt('0') // false 至少匹配其中一个模式时返回true
f('0') // 抛出MatchError
Scala XML
scala内建支持XML格式
节点序列的类型为NodeSeq
var xml = <html><head></head><body></body></html>
for (elem <- xml.child) { // 返回xml的子节点
println(elem)
}
可以使用NodeBuffer的形式在程序中构建NodeSeq:
var nodeItems = new NodeBuffer
nodeItems += <ul></ul>
var nodes: NodeSeq = nodeItems // NodeSeq为不可变类型,NodeBuffer可以被隐式转换为NodeSeq
通过attributes方法获取节点中的属性:
var node = <img src="image.jpg"></img>
var imgSrc = node.attributes("src") // 结果为image.jpg,类型为节点序列,不是字符串
imgSrc.text // 转换类型为字符串
for (attr <- elem.attributes) {} // 遍历attr.key和attr.value.text
elem.attributes.asAttrMap // 将属性转换为Map类型
XML内嵌表达式:
var items = Array("Hello", "World")
val elem = <ul><li>{items(0)}</li><li>{items(1)}</li></ul>
println(elem) // 结果为<ul><li>Hello</li><li>World</li></ul>
XPath匹配XML节点:
val x = elem \ "li" // 返回elem直接所有一级子节点为li的节点
val x = elem \\ "li" // 返回elem所有类型为li的子节点
val x = elem \ "_" \ "li" // 返回elem所有二级子节点,且类型为li
val x = elem \ "@src" // 返回elem节点的src属性
val x = elem \\ "@src" // 返回elem节点所有子类节点的src属性
从文件中加载XML
import scala.xml.XML
val xmlFile = XML.loadFile("xmlFile.xml")
泛型类
scala使用中括号表示泛型类:
class Pair[T, S] (val first: T, val second: S)
泛型函数:
class getMiddle[T](arr: Array[T]) = arr(arr.length / 2)
val fun = getMiddle[String] _ //返回一个函数,但是类型被限制为String
泛型上界:
[T <: Type] T必须为Type的子类
泛型下界:
[T >: Type] T必须为Type的超类
视图界定:
[T <% Type] T可以被隐式转换成Type
基本类型数组泛型指定:需要将上界指定为Manifest
协变:对于Pair[+T],如果A是B的子类,那么Pair[A]是Pair[B]的子类。
逆变:对于Pair[+T],如果A是B的子类,那么Pair[A]是Pair[B]的父类。
函数在参数上是逆变的,在返回值上是协变的。对于某个对象的消费适合逆变,而对于他的产出则适用于协变。
单例类型:
class A {
def funA() = {
...
this
}
}
class B extends A {
def funB() = {
...
this
}
}
val b = new B
b.funA.funB // 会出错,funA返回的this类型为A,A没有funB方法
将类定义修改为:
class A {
def funA() = {
...
this.type
}
}
class B extends A {
def funB() = {
...
this.type
}
}
val b = new B
b.funA.funB // 无问题
object A
class B {
def funA(obj: A.type) = {} // 指代的是A这个单例对象而不是A这个类型
}
类型别名:
type MyType = HashMap[Int, String]
结构类型:
def fun(obj: {def f(s: String): Any}) {}
// 具有f(s: String): Any方法的类的实例才能作为该方法的参数。(scala使用反射来调用该方法)
复合类型:T1 with T2 with T3 ...
自身类型:
trait MyExceptionTrait {
this: Exception =>
...
// this被当做Exception对象来对待
}
MyExceptionTrait 特质只能被混入Exception及其子类中
抽象类型:
trait A{
type Content
def make(): Content
}
class B extends A {
type Content = String // 必须指定类型
def make():String = {}
}
隐式转换:
package com.paul
class Fraction(val x:Int, val y: Int) {
def *(another: Fraction) = {...}
}
object Fraction {
def apply(val x:Int, val y: Int) = new Fraction(x, y)
}
package com.paul
object FractionConversion {
implicit def int2Fraction(n: Int) = Fraction(n, 1)
}
class Demo extends App {
import com.paul.FractionConversion._
val result = 3 * Fraction(3, 2) // 隐式调用int2Fraction
}
隐式参数:
case class Delimeter(val left: String, val: right: String)
class Tools {
implicit val delimeter = Delimeter("<<", ">>")
def makeString(s: String)(implicit delim: Delimeter) = {
// demimeter会被隐式的赋予delim
}
}