[关闭]
@spiritnotes 2016-03-22T11:16:33.000000Z 字数 13347 阅读 1890

《Scala编程》

Scala


1 可伸展的语言

1.1 容易扩展

增加新的类型
增加新的控制结构:例子基于Actor的并发编程

1.2 为什么扩展

面向对象与函数式编程的组合

1.3 为何选择Scala

2 Scala入门初探

2.1 学习使用Scala解释器

2.2 变量定义

val
var
自动类型推断

2.3 函数定义

递归函数中,返回类型必须被明确说明

2.4 编写Scala脚本

2.5 用while做循环,用if做判断

2.6 用foreach和for做循环

3 Scala入门再探

3.1 使用类型参数化数组

方法只有一个参数,调用的时候可以省略点和括号
Scala没有操作符重载,因为其没有传统意义上的操作符,其很多字符都可以作为方法名

3.2 使用列表

3.3 使用元组

3.4 使用集和映射

3.5 学习是被函数式风格

函数式风格帮助写出更易读懂,同样也更不容易犯错的代码

3.6 从文件里读取文件行

  1. scala.io.Source
  2. val v:Iterator[String] = Source.fromFile(path).getLines // .toList

4 类与对象

4.1 类、字段和方法

类定义中,可以放置字段和方法,统称为成员。字段,不管是val还是var,都是指定对象的变量。方法,包含了可执行的代码。字段保留对象的状态或数据,而方法使用这些数据执行对象的运算工作。类被实例化时,运行环境会预留一些内存来保留对象的状态映像-即变量的内容。字段的另一种说法是实例变量。

4.2 分号推断

单行分号是可省略的
跨越多行时,Scala多数情况能在正确的位置分割语句,可以加括号或者将中缀操作符放置末尾

4.3 单例对象

除了以object外,与类定义差不多。
单例对象与某类共享同一个名称时,就称为该类的伴生对象。其必须定义在同一个文件里。可以相互访问私有成员。

new只能用在伴生类上用以创建对象。单例对象不带参数,因为其不是通过new创建的。每个单例对象实现为虚构类(synthetic class,名字是伴生类+$)的实例,并指向静态的变量,与Java的静态类有着相同的初始化语义。单例对象在第一次访问时才被初始化。

4.4 Scala程序

创建含main函数的独立对象。
如果不是脚本,推荐按照类名命名文件名,易于寻找。脚本以结果表达式结束。

程序执行方法
以scalac编译文件/或fsc快速scala编译器,以后台方式运行
scala name

4.5 Application特质

直接extends Application输入代码执行
Application声明了合适签名的main方法
花括号之间的代码被收集进了单例对象的主构造器,并在类被初始化时执行

5 基本类型与操作

5.1 基本类型

5.2 字面量

5.3 操作符和方法

重载:参数个数或类型不一样的多个方法
任何方法嗾使操作符
后缀操作符是不用点或括号调用的不带任何参数的方法,一般是有副作用加上括号

5.4 数学运算

5.5 关系和逻辑操作

< ...
&& || !

5.6 位操作符

& | ^ ~ << >> >>>

5.7 对象相等性

== !=
检查左边是否为null,不是则调用其equals方法
Java里的引用的==是比较引用相等性

5.8 优先级与关联性

根据操作符的第一个字符判断优先级

5.9 富包装器

6 函数式对象

6.1 类Rational的规格说明书

6.2 创建Rational

类没有主体,不需要指定空的花括号

6.3 重新实现toString方法

6.4 检查先决条件

6.5 添加字段

6.6 自指向

this

6.7 辅助构造器

this

6.8 私有字段和方法

定义私有gcd方法以及val g=gcd值用以分子分母化简

6.9 定义操作符

def +(that:R):R =
def *(that:R):R =

6.10 Scala的标识符

字符数字标识符:字母数字, $保留给编译器使用
尽量少使用
,保持与Java一致且_有很多非标识符用法
习惯上常量也是驼峰写法
操作符标识符:由一个或多个操作符字符组成
混合标识符:由字母数组组成,后面跟一个下划线和一个操作符标识符 unary_-, myvar_=
字面量标识符:使用``包含的任意字符串

6.11 方法重载

6.12 隐式转换

implicit def intToRational(x:Int) = new...

7 内建控制结构

7.1 if表达式

使用val v = if ... else ... 方式赋值

7.2 while循环

7.3 for表达式

7.4 使用try表达式处理异常

7.5 匹配表达式

7.6 break与continue

7.7 变量范围

7.8 重构指令式风格的代码

8 函数与闭包

8.1 方法

某个对象的成员

8.2 本地函数

把函数定义在其他函数里面。

8.2 头等函数

函数值是对象

8.3 简化

8.4 占位符语法

8.6 部分应用函数

可以将方法或嵌入函数转变为对象

8.7 闭包

定义
    封闭项
        不带自由变量的函数字面量
    开放项
        带自由变量的函数字面量
    依照函数字面量在运行时创建的函数值(对象)就称为闭包,名字源于捕获自由变量的绑定从而对函数字面量执行的“关闭”行动
    得到的函数值中将包含指向捕获的自由变量的索引
    闭包捕获了变量本身,而不是变量指向的值
        变量改变,闭包也改变
        闭包也可以改变外部变量的值
        闭包如由函数创建,其绑定对象为函数调用当次的本地变量

8.8 重复参数

(args:Int*)
可以指定最后一个参数是重复的,变长的
函数内部,重复参数的类型是声明参数类型的数组 Array[T]
使用时需要产生参数序列 sum(1 to 5 :_*)

8.9 尾递归

tail recursive
递归调用是函数体执行的最后一件事
编译器自动优化,无需任何开销
调试
    关闭尾递归 -g:notailcalls
局限
    仅仅优化直接递归调用返回同一函数
    所有间接都不支持

9 控制抽象

9.1 减少代码重复

一个读取文件列表并过滤出文件的例子
使用高阶函数
非函数式方法中,通用为函数,非通过也就是参数部分为数据
而使用高阶函数作为参数,可进一步提高抽象

9.2 简化客户代码

使用高阶函数如exists简化代码

9.3 currying化

将原来接受多个参数的函数变成接受一个参数的函数的过程
新函数返回一个以原来第二个参数作为参数的函数
def A(x:Int)(y:Int) = x*y
    可以理解为 def a(x:Int) = (y:Int)=> x+y,但两者不同
    可以通过占位符获得参考  val z=a(1)_

9.4 编写新的控制结构

twice方法
withclose方法 --》借贷模式

9.5 传名参数(by-name parameter)

原始
    def f(g:()=>Unit) = {...}
    f{ ()=> printle(....) }
参数的类型不要使用 g:()=>, 使用 g:=>
优化
    def f(g:=>Unit) = {....}
    f { println(...) }
实例
    def until(condition:=>Boolean)(g:=>Uint) {.....}
    until (x == 10) { x -= 1 ....}
如果要在带名函数中使用return,必须定义其返回类型

10 组合与继承

完成一个练习项目

11 Scala的层级

11.1 Scala的类层级

11.2 原始类型是如何实现的

11.3 底层类型

Any
    AnyVal
        基本数据类型
        Boolean
        Unit
    AnyRef
    所有类型的子类
        Null
            实例 null
        Nothing
            标明不正常的终止
            如异常返回

12 特质

12.1 特质是如何工作的

弥补多重继承导致的问题
    对基类共同成员的初始化
    虚拟基类
封装方法和字段的定义,混入到类中重用
特质不能有任何“类”参数,即主构造器不能有参数
spuer调用是动态绑定,而类是静态绑定的

12.2 廋接口对阵胖接口

把廋接口变成胖接口

12.3 样例:长方形对象

12.4 Ordered特质

class X extends Ordered[X]{
def compare(that: X) = this.v - that.v
}
自动帮助实现各种不等式操作,equals仍然需要自己实现

12.5 特质用来做可堆叠的改变

通过super调用来堆叠改变
可以为类或对象添加多个相互调用的特质,从最后一个开始
使用super,子类会调用父类的同名函数
从右到左
当需要调用super而实际super为抽象特质时,则将实现作为抽象
    abstract override def log(...) {}

12.6 为什么不是多继承

多继承不能线性化

12.7 特质用还是不用

如果行为不会被重用,则具体类
如果要在多个不相关的类中重用,则特质
希望从Java中继承它,使用抽象类
计划以编译后方式发布它,则外部组织能够写一些继承自它的类,则抽象类
效率重要,使用类,Java中类虚方法调用快于接口方法调用

13 包和引用

13.1 包

采用Java平台完整的包机制
几种方式
    C#方式:  package x{ calss x ... package y { ... }}
    package x.y;package z 位于文件顶端,不带方括号
包可以嵌套
    内部作用域里面的包可以隐藏外部作用域里面的同名包
    可以访问上层作用域中的名称
    需要访问非直接作用域中的名称,使用绝对包名 _root_.
    包声明链 x.y.z 不会使中间包 x 和 x.y 变成可见

13.2 引用

目的:使用更短的名称
可以引入包、类以及对象
    可以在def函数引入具体类实例 def ..(f:F){import f._}
    _ 作为通配符使用
可以出现在任何位置,将其放在需要的地方,避免名字冲突
可以重命名和隐藏特定成员
    选择器 .{A,B}
    重命名 .{A=>B, C}
    隐藏 .{A=>_, _}

13.3 隐式引用

java.lang / scala / Predef 总是被引入

13.4 访问修饰符

私有成员
    与Java相同
    该成员只能在包含了成员定义的类或对象内部可见
    内部类也是一样,私有成员外部类不可访问
保护成员
    只在定义了成员的类的子类中可以被访问
    Java中同一个包中其他类也可以访问,Scala更严格
公开成员
    任何地方可以访问
访问作用域
    可以通过使用限定词强调 private[x]
    private 限定词还可以指向所属类或对象
    private[this] 仅能在包含了定义的同一个对象中访问,对象私有
    protected[X] 允许子类以及修饰符所属的包、类或对象X访问带有此标记的定义

14 断言与单元测试

14.1 断言

assert(condition)
assert(condition, explanation)
ensuring判断
    对结果进行判断 位于结果之后 = {} ensuring( ... _ ...)
JVM -ea -da 进行控制

14.2 单元测试

scalatest
    Suite
    FunSuite
        以函数值的方法定义测试,重载了execute

14.3 失败报告

 === 
expect() { ... }
intercept(calssof[异常类])

14.4 JUnit和TestNG

14.5 规格测试

14.6 基于属性的测试

14.7 组织和运行测试

15 样本类与模式匹配

15.1 简单例子

样本类
case class 修饰
Scala编译器自动为你的类添加一些句法上的便捷设定
    与类名一致的工厂方法
    类参数列表中的所有参数隐式获得val前缀,当作字段维护
    添加toString、hashCode、equals的“自然”实现,做结构化的比较
模式匹配
选择器 match {备选项}  用以替代 switch {选择器} {备选项}
通过以代码编写的先后词项尝试每个模式来完成计算
特点
    match为表达式,以值为结果
    不会掉到下一个case中
    没有模式匹配,MatchError会被抛出

15.2 模式的种类

通配模式
    case _ => ... 匹配任何对象
    case BinOp(_, _, _) 忽略对象中不关心的部分
常量模式
    匹配自身,任何字面量
        “true”、5
        val或单例对象(如Nil)
变量模式
    类似通配符,可匹配任意对象
    把变量绑定在匹配的对象上
    case x => use x
    以小写字母开始的简单名被当作是模式变量
    对于使用val定义的小写字母变量,可使用`name`,将其变为常量
构造器模式
    假如其名字指向样本类,则首先检查该对象是该名称的样本类的成员,然后检查对象的构造器参数是符合额外提供的模式的
    支持深度匹配
序列模式
    固定长度
        List(_, _, _)
    任意长度
        List(0, _*)
    注意类型,使用List只能匹配List及其子类,可使用Array等
元组模式
    case (a,b,c) => 
类型模式
    case s:String =>
    case m: Map[_,_] =>
        类型擦除,运行期时其泛型已经不可见
        Array除外,特殊处理
变量绑定
    变量名@模式,匹配成功后,将变量设置为匹配的对象

15.3 模式守卫

模式守卫接在模式之后,开始于if
可以是任意引用模式中变量的布尔表达式,返回true才匹配成功
case n:Int if 0 < n =>

15.4 模式重叠

全匹配的样本要跟在更具体的简化方法之后

15.5 封闭类

封闭类除了类定义所在的文件之外不能再添加任何新的子类
使用继承自封闭类的样本类做匹配,编译器将通过警告信息标志出缺失的模式组合
如果匹配未用全部case class,可以为匹配表达式添加 @unchecked 注解进行警告抑制
    (e: @unchecked) match {...}

15.6 Option类型

两种形式
    Some(x)
    None
集合类的 get 方法就是返回 Option 对象
分离可选值
    使用模式匹配
        x match {case Some(s)=>s; case None=> ...}

15.7 使用

拆分元组
    val (x,y) = tuple
解构样本类的对象
    val Bin(op, left, right) = binOject
用作偏函数的样本序列
    样本序列可以用作能够出现函数字面值的任何地方
    样本序列可以多个入口点,每个都有自己的参数列表
    val withDefault: Option[Int] => Int = {case Some(x): x; case None => 0}
        withDefault(Some(10))
        withDefault(None)
        两者都自动转为了Option类
    Actor中使用广泛
    偏函数
        val second: PartialFunction[List[Int], Int] = {case x::y::_ => y}
        相当于定义一个偏函数类型,会有apply和isdefinedAt两个函数
        def apply() = xs match {case x::y::_ => y}
        def isDefinedAt(xs:List[Int]) = xs match {case x::y::_ => true; case _ => false}
for
    映射的键值对
    for (Some(x) <- listofOptions) // None的值不会被匹配

15.8 一个更大的例子

16 使用列表

16.1 列表字面值

List(1,2,3) ....

16.2 类型

同质的
列表类型是协变的
空列表 List()/Nil 类型为 List[Nothing],是所有List类型的子类

16.4 基本操作

List(2,3):::List(3,4)
cons: 2::LIst(3,4)
drop(n) / dropRight(n) 去掉左右n个元素
count / filter / exists / foreach / forall / isEmpty / length / map / mkString / reverse / sort
head / tail / init 前n-1个元素

16.5 列表模式

拆分
    var List(a,b,c) = list
    var a::b::rest = list

17 集合类型

17.1 集合库概览

层级结构
    Iterable
        Seq
            List
                头部添加删除快
                不可变
            数组Array
                随机访问快速
            列表缓冲 ListBuffer
                += 元素添加
                +: 前缀添加
                toList 到 List
            数组缓存
            队列 Queue
                enqueue
                    item 添加元素
                    items 添加多个元素
                dequeue
                    返回对偶
                += / ++=
            栈Stack
                push
                top
                pop
            RichString
                Seq[Char]
        Set
            集合
                含有可变与不可变多种
                创建
                    val v = Set(a,b,c)
                访问
                    v += d
                        可变加入自身
                        不可变产生一个新的set
                + / - / ++ / -- / ** 交集 /size / contains / Set.empty[String] / +=  /  -=  / ++=   / --= /clear
        Map
            映射
                创建
                    Map('c'->2 ... )
                    Map(('c',2)....)
                    mutable.Map.empty[String, Int]
                    有可变与不可变
                        scala.collection.mutable.Map
                        scala.collection.mutable.HashMap
                        scala.collection.immutable.SortedMap
                        scala.collection.mutable.LinkedHashMap
                        默认获得HashMap
                访问
                    m('a')
                    if (m.contains('a')) ...
                    m.getOrElse('a', 0)
                    m.get('a') 返回一个Option对象
                修改
                    m += ('4'->2, ...)
                    m -= 'a'
                    m = m1 + ('a'->4, ....)
                    ++  /  --  /  size  / contains / isempty  /  ++=  / --=
                遍历
                    for ((k,v) <- m) ...
                    for (k <- m.keySet)
                    for (v <- m.values)
                    for ((k,v) <- m) yield (v,k)
        特点
            不可变集合和映射返回的类型和其初始化个数有关
            Iterable的elements返回迭代器
                hasNext
                next
        有序集和映射
            只有可变版本
            由红黑树实现, TreeSet、TreeMap
            SortedMap、SortedSet
        同步的集和映射
            val map:Map[Int,Int] = new HashMap[Int,Int] with SynchronizedMap[Int,Int]
说明
    可以在实际中实验使用可变与不可变
    初始化集合时候可以赋值新的类型
        List[Any](42)
    toList / toArray 转变成为List和数组、
元组
    用来聚集值,可为异构多类型
    创建
        v = (1,"abs, 4.0)
        val t = new Tuple3[Int, Int, String]
        v = Tupel2(1,2)
    访问
        访问使用 t._1, t._2, t._3

        因为可能类型不一样,因此不能使用apply方式访问
    val (first, second, _) = t
        必须有括号,否则为多个元素赋同样的值
    zip可将Array合并成元组的Array
        keys.zip(values).toMap

18 有状态的对象

var s 隐含公开的getter和setter

private[this] var h
def hour:Int = h
def hour_=(v:Int) {h = v}

赋值 = _ 表示使用该类型的默认零值

19 类型参数化

19.1 queues函数式队列

具有以下三种操作方式的数据结构
    haed
    tail
    append
    是不可变的

19.2 信息隐藏

主构造器可以为private,这样只能类以及伴生对象可以访问,可以通过辅助构造器来对外
class x(xxx) private{ ... }

变化型注解

协变
    是指子类型化的类型有类型参数具有同样的超类子类关系
    泛型的参数类型被当做方法参数的类型,包含它的类或特质就可能不能与这个类型参数一起协变
        如Queue[Int],其某方法append是针对Int的,将其赋值为Queue[Any]后调用该方法还是用的针对Int的
    如何检查
        将类或者特质结构体的所有位置分为正、负、中立
        位置是指类/特质的结构体内可能用到类型参数的地方
        +的类型参数可以放在正的位置,其他类似
逆变
    子类型化的类型与类型参数协变关系相反
    例子:输出通道 输出Output[Any]是OutPut[Int]的子类
    Function1特质
        Function1 [-S, +T]{ def apply(x:S):T }
        参数是逆变的,返回值是协变的
+-被称为变化型注解
Java数组是支持协变的,其运行时保存了数组的元素类型
下界
    U >: T定义T为U的下界,U必须是T的超类
    class Queue[+T]  ...  def append[U >: T] = new Queue[U] ...

20 抽象成员

种类
    类型成员
        可以不定义具体的类型
            type T
            定义为子类的抽象类型
        为类型定义短小的,具有说明先的别名
        抽象类型
            函数的参数不支持协变, x(d:F)与x(d:C)不是重写
                Animal eat Food
                Cow eat Grass
                animal = Cow, animal eat Fish
            Animal {type SuiteAbleFood <: Food ; def eat(food:SuiteableFood)}
            Cow extends Animal {type SuiteableForrd = Grass; override def eat(food: Grass)}
        路径依赖类型
            类型可以作为对象的成员
                可以理解为type语句是可执行的
            类中的内部类与具体实例相关,不同实例是不同类
                new o1.Inner
            枚举 scala.Enumeration即是使用了内部类
    抽象val
        val init:String
        定义类型不赋值
        同def init:String,但限制了不可改变
    抽象var
        var hour:Int
        实际上隐式声明了其getter和setter方法
初始化问题
    由于特质无构造器参数,如果特质的构造器需要使用到抽象val,而其在子类中定义
    fields预初始化字段
        调用超类之前初始化子类的字段
        new {val ...} with RationalTrait
        object t extends {val ...} with Ratianal
        其this指向的是包含正在构造的类或对象的对象,不是被构造对象本身
    懒加载val
        初始化延迟到第一次使用的时候
        由于是按需初始化,文本顺序不用考虑

21 隐式转换与参数

优点
    比扩展方法更灵活、简洁
    支持目标类型的转换
        即是说可以自动对参数进行转换
    不显式书写利于代码的进化
规则
    标记规则:只有标记为implicit的定义才是可用的,可以标记变量、函数、对象定义
    作用域规则:插入的隐式转换必须以单一标识符的形式处于作用域中,或与转换的源或目标类型关联在一起
        源类型和目标类型的伴生对象中
    无歧义规则:隐式转换唯有不存在其他可插入转换的前提下才能插入
        c1(x)+y /// c2(x)+y 有歧义
        可去掉一个
        明确写出想要的转换
    单一调用原则:只会尝试一个隐式操作
        不会出现c1(c2(x)) + y
    显式操作先行规则:如编写的代码类型检查无误,则不会尝试任何隐式操作
何处尝试
    转换为期望类型
        如 val c:Int = 3.5
    指定(方法)调用者的转换
        用途
            接受者转换使得新的类可以更为平滑地集成到现有类层级中
                整数 + 有理数
            支持在语言中编写领域特定语言 DSL
                -> 是类ArrowAssoc的方法,定义了从Any到ArrowAssoc的隐式转换
        如 obj.doIt,而obj无doIt方法,则其会转换
    隐式参数
        被提供的是完整的最后一节柯里化参数
        implicit关键字是用于全体参数列表,而不是单独的参数
            def x()(implicit x:X, y:Y)
            implicit val x:X = new ...
            implicit val y:Y = new ....
        方式是匹配参数类型与作用域内的隐式值类型
        常用来提供转换信息
            def x[T](...)(implicit order:T=>Ordered[T]):T=...
            省略到代码里面对order的调用,则代码中完全不见order
                implicit在参数上,说明该函数值是隐式可访问的单个标识符
    视界
        def x[T <% Ordered[T]](...) = ...
        任意T都好,只要能将T当做Ordered[T]即可
如何调试
    明确写到代码中测试
    scalac -Xprint:typer

22 实现列表

原理
    List是抽象类
    类型参数是协变的
    所有列表操作都可以通过 isEmpty/ head/ tail三种基本方法定义
    Nil 为 case object, :: 为final case
        ::利于模式匹配
    ::  ::: 绑定在右操作数上
ListBuffer
    对 += 以及 toList 都只要常量时间即可完成

23 重访For表达式

所有能产生值的for表达式都被转换成map、flatmap、filter的组合调用
不要yield的被转为fikter和foreach
具体
    带一个生成器的for
        for (x<-e)yield y
        e.map(x=>y)
    带if
        for (x<-e if y) yield z
        e.filter(y).map(z)
    两个生成器开头的
        for (x<-xs;y<-ys;seq) yield z
        xs.flatMap(ys.map(seq z))
    包含模式
        直接转为模式
map、flatMap、filter也可以使用for实现
    for (x<- xs) yield f(x)
    for (x <- xs; y <- f(x)) yield y
    for (x <- xs if p(x)) yield x
泛化For
    列表、数组以及很多类中都支持map、flatMap等
        Range/Iterator/Stream/Set
    类型
        定义了map
            支持单一生成器组成的for表达式
        定义flatmap和map
            支持若干个生成器组成的for
        定义foreach
            允许for循环,单个或多个生成器
        定义filter
            允许if开头的过滤器表达式
    for转译发生在类型检查之前
    方法签名
        class C[A]
        def map[B](f:A=>B):C[B] = {}
        def flatMap[B](f:A=>C[B]):C[B]
        def filter(p:A=>Boolean):C[A]
        def foreach(b:A=>Unit):Unit

24 抽取器(Extractors)

目的:为已经存在的类型定义新模式,而这种模式不需要遵守类型的内部表达式
email
    object Email extends (String,String)=>String {...}
    注入方法,生成指定子集的元素
        def apply(user:S,domain:S) = user +"@"+domain
    抽取方法,传入相同子集源并抽取部分
        def unapply(str:S):Option[(S,S)]={...Some(x,y) else None}
    应该满足 X.unapply(X.apply(x,y)) == Some(x,y)
    case Email(Twice(x @ UpperCae()), domain) => 
0个或1个变量的模式
    1个返回 Option[X]
    0个返回Boolean,为true或false
变参抽取器
    def apply(parts:X*):X ...
    def unapplySeq(x:X):Option[Seq[Y]] ....
    同样可以返回类型为 Option[(String, Seq[String])]
    Seq的匹配采用X(a, b, _*)
    List、Array的匹配器是其对象中的unapply定义的
抽取器可以让模式与数据表达无关,表征独立
    样本类易于建立、定义,代码更少
    样本类用于匹配更为高效
    如果样本类继承自sealed基类,则编译器会检测出
正则表达式
    位于 scala.util.matching.Regex, Regex(string)
    原始字符串可以避免多次输入反斜杠
    string.r 直接生成正则表达式
        val Decimal = """(-1)?(\d+)(\.\d*)?""".r
    方法
        .findFirstIn str
        .findAllIn str
        regex findPrefixOf str
    对每个正则表达式都定义了抽取器
        val Decimal(sign, int, dec) = "-1.23"
        可以用于for语句和模式匹配中

25 注解

why
    元编程工具
        使用scaladoc产生文档
        漂亮地打印出符合你偏爱风格的代码
        代码的常见错误加成
        实验性类型检查
        把程序当做输入程序
    注解通过让程序员将需要传递给工具的指令标示在它们的源代码中以提供对这些工具的支持
    例如
        文档中标记为废除
        文件检查器关闭
        副作用检测器可以得到指示,验证指定的方法是否具有副作用
    语法
        用于任何的声明或定义上
            @deprecated def 。。。
        用于表达式上
            (e:@unchecked) match ...
        一般形式
            @annot(exp1....){val name1=const1 ....}
            无参数可以省略()
                @xx 与 @xx() 是一样的
    标准注解
        废弃
            @deorecated 
        易变
            @volatile
        二进制序列化
            @serializable
            @SerialVersionUID(1234)
            @transient
        get/set
            @scala.reflect.BeanProperty
        不检查
            @unchecked

26 XML

特点
    嵌套的标签系统
    开始标签可以有属性
XML字面量
    Scala支持表达式为单个XML,不需要加引号
类
    Node类,单个元素的NodeSeq
    Text类,只包含文本的节点
    NodeSeq类,节点序列
        xml.NodeSeq.Empty
转义符{}
    可以在XML中使用转移符,且可以嵌套
    {} 中可以生成NodeSeq节点或Scala结果,其被转为字符串插入
    以打印方式输出,{}中的xml关键字符会被转义
    要显式 { } 则必须重复2次
可以为类增加 toXML 方法
拆解
    抽取文本  .text
    抽取子元素 
        \ "b" 抽取子标签为 "b" 的元素
        \\ "b" 在所有Node中深度搜索
    抽取属性  \ "@name"
其他
    toString
    scala.xml.XML.saveFull
    scala.xml.XML.loadFile
模式匹配
    与XML字面量相似,差别在于插入转义括号 {}
        case <a>{c}</a>
        case <a>{c@_*}</a> 对子元素序列进行匹配
    for (therm @ <cc>{_*}</cc> <- therms)

27 使用对象的模块化编程

28 对象相等性

原理
    Java中 == 对于引用表示 对象一致性
    Scala中 eq 表示引用一致性, == 等于 equals
陷阱
    以错误的签名定义 equals 方法
        重载是按照参数的静态类型而不是运行期类型来解析
        equals 函数参数类型应该定义为 Any
        有可能错误地以  == (X)  重载了Any中的final ==(Any) 
    修改equals但没有同时修改 hashcode
        hashCode与equals总应该一同定义
        hashCode只能依赖equals方法依赖的字段
    用可变字段定义equals
        放入集合中,再改变该值,则该对象无法查找到,而elements中含有
        集合中的contains是针对每个元素与新元素进行equals比较
    未能按等同关系定义 equals 方法
        要求对等关系
            自反射性 a==a
            对称性
            传递性
            一致性
            对于非空x, x.equals(null) = false
        父类和子类
            新增元素后,不满足对称性
            解决
                宽松
                    子类的equals中分别针对子类、父类进行模式匹配判断
                    现在不可传递了 childa = father = childb
                更明确
                    this.x == that.x && this.getClass == that.getClass
                    对 new P(1,1){override val y = 2} 太过严格
                canEquals
                    通过定义canEqual函数来识别
                    重定义equals时同时重定义canEqual
                    def canEqual(other:Any) = other.isInstanceOf[Point]
                    def equals() ... case that:Point => (that canEqual this) && ...
                    违反里氏替代原则,set contains cp // set contains p

29 结合Scala和Java

30 Actor和并发

actor
    actor是一个类似线程的实体,有一个用来接受消息的邮箱
    创建
        继承scala.actors.Actor并完成act方法
        scala.actor.Actor对象中的actor方法创建actor
            val x = actor { ... code ...}
            直接运行,不需start方法
        启动 .start()
    消息
        发送:  x ! "hello"
        接受 : receive {  case ...  }
        发送消息不会被阻塞,接受消息不会被打断
        消息处理
            actor只会处理传给receive方法的偏函数中的某个样本相匹配的消息
            对于每个消息,调用传入的偏函数的 isDefinedAt 判断是否与某个样本匹配,然后处理该消息
            receive 方法将选定邮箱中第一个让 isDefinedAt 返回true的消息
            偏函数的apply方法将应用到该消息上
            无匹配消息,则actor阻塞,直到收到匹配的消息
    原生线程当做actor
        scala.actors.Actor.self
        self ! 5
        self.receive{ case x=> x }
        .receiveWithin(misecends ) { ... }
    重用线程
        react函数
            react找到并处理消息后并不返回
            返回类型是Nothing
            在后台,react完成时抛出一个异常
            不需返回,因此不需要保留当前线程的调用栈
            react 函数每个case里面再调用 act()
            loop { react { ...... }}
    良好风格
        Actor不应阻塞
            处理消息时不应该阻塞
            应该允许某种表示动作可以执行的消息发给它
        只通过消息与actor通信
        优先选择不可变消息
        让消息自包含
            消息中包含冗余消息,记录上下文便于消息回来后的处理
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注