| 原文地址、续 
 课程内容:
 
 
 关于这节课表达式值函数类继承特质类型apply方法单例对象函数即对象包模式匹配样本类try-catch-finally
 
 
 关于这节课
 最初的几个星期将涵盖基本语法和概念,然后我们将通过更多的练习展开这些内容。
 有一些例子是以解释器交互的形式给出的,另一些则是以源文件的形式给出的。
 安装一个解释器,可以使探索问题空间变得更容易。
 
 为什么选择 Scala?
 ·表达能力
 ·函数是一等公民
 ·闭包
 ·简洁
 ·类型推断
 ·函数创建的文法支持
 ·Java互操作性
 ·可重用Java库
 ·可重用Java工具
 ·没有性能惩罚
 
 Scala 如何工作?
 ·编译成Java字节码
 ·可在任何标准JVM上运行
 ·甚至是一些不规范的JVM上,如Dalvik
 ·Scala编译器是Java编译器的作者写的
 
 
 用 Scala 思考
 Scala不仅仅是更好的Java。你应该用全新的头脑来学习它,你会从这些课程中认识到这一点的。
 
 启动解释器
 使用自带的sbt console启动。
 
 复制代码$ sbt console
[...]
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
表达式
 
 res0是解释器自动创建的变量名称,用来指代表达式的计算结果。它是Int类型,值为2。复制代码scala> 1 + 1
res0: Int = 2
Scala中(几乎)一切都是表达式。
 
 值
 你可以给一个表达式的结果起个名字赋成一个不变量(val)。
 
 你不能改变这个不变量的值.复制代码scala> val two = 1 + 1
two: Int = 2
 变量
 如果你需要修改这个名称和结果的绑定,可以选择使用var。
 
 复制代码scala> var name = "steve"
name: java.lang.String = steve
scala> name = "marius"
name: java.lang.String = marius
 函数
 你可以使用def创建函数.
 
 在Scala中,你需要为函数参数指定类型签名。复制代码scala> def addOne(m: Int): Int = m + 1
addOne: (m: Int)Int
 如果函数不带参数,你可以不写括号。复制代码scala> val three = addOne(2)
three: Int = 3
 复制代码scala> def three() = 1 + 2
three: ()Int
scala> three()
res2: Int = 3
scala> three
res3: Int = 3
匿名函数
 你可以创建匿名函数。
 
 这个函数为名为x的Int变量加1。复制代码scala> (x: Int) => x + 1
res2: (Int) => Int = <function1>
 你可以传递匿名函数,或将其保存成不变量。复制代码scala> res2(1)
res3: Int = 2
 如果你的函数有很多表达式,可以使用{}来格式化代码,使之易读。复制代码scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>
scala> addOne(1)
res4: Int = 2
 对匿名函数也是这样的。复制代码def timesTwo(i: Int): Int = {
  println("hello world")
  i * 2
}
 在将一个匿名函数作为参数进行传递时,这个语法会经常被用到。复制代码scala> { i: Int =>
  println("hello world")
  i * 2
}
res0: (Int) => Int = <function1>
 部分应用(Partial application)
 你可以使用下划线“_”部分应用一个函数,结果将得到另一个函数。Scala使用下划线表示不同上下文中的不同事物,你通常可以把它看作是一个没有命名的神奇通配符。在{ _ + 2 }的上下文中,它代表一个匿名参数。你可以这样使用它:
 
 复制代码scala> def adder(m: Int, n: Int) = m + n
adder: (m: Int,n: Int)Int
你可以部分应用参数列表中的任意参数,而不仅仅是最后一个。复制代码scala> val add2 = adder(2, _:Int)
add2: (Int) => Int = <function1>
scala> add2(3)
res50: Int = 5
 柯里化函数
 有时会有这样的需求:允许别人一会在你的函数上应用一些参数,然后又应用另外的一些参数。
 例如一个乘法函数,在一个场景需要选择乘数,而另一个场景需要选择被乘数。
 
 你可以直接传入两个参数。复制代码scala> def multiply(m: Int)(n: Int): Int = m * n
multiply: (m: Int)(n: Int)Int
 你可以填上第一个参数并且部分应用第二个参数。复制代码scala> multiply(2)(3)
res0: Int = 6
 你可以对任何多参数函数执行柯里化。例如之前的adder函数复制代码scala> val timesTwo = multiply(2) _
timesTwo: (Int) => Int = <function1>
scala> timesTwo(3)
res1: Int = 6
 复制代码scala> (adder _).curried
res1: (Int) => (Int) => Int = <function1>
可变长度参数
 这是一个特殊的语法,可以向方法传入任意多个同类型的参数。例如要在多个字符串上执行String的capitalize函数,可以这样写:
 
 复制代码def capitalizeAll(args: String*) = {
  args.map { arg =>
    arg.capitalize
  }
}
scala> capitalizeAll("rarity", "applejack")
res2: Seq[String] = ArrayBuffer(Rarity, Applejack)
类
 
 上面的例子展示了如何在类中用def定义方法和用val定义字段值。方法就是可以访问类的状态的函数。复制代码scala> class Calculator {
     |   val brand: String = "HP"
     |   def add(m: Int, n: Int): Int = m + n
     | }
defined class Calculator
scala> val calc = new Calculator
calc: Calculator = Calculator@e75a11
scala> calc.add(1, 2)
res1: Int = 3
scala> calc.brand
res2: String = "HP"
 构造函数
 构造函数不是特殊的方法,他们是除了类的方法定义之外的代码。让我们扩展计算器的例子,增加一个构造函数参数,并用它来初始化内部状态。
 
 注意两种不同风格的评论。复制代码class Calculator(brand: String) {
  /**
   * A constructor.
   */
  val color: String = if (brand == "TI") {
    "blue"
  } else if (brand == "HP") {
    "black"
  } else {
    "white"
  }
  // An instance method.
  def add(m: Int, n: Int): Int = m + n
}
你可以使用构造函数来构造一个实例:
 
 复制代码scala> val calc = new Calculator("HP")
calc: Calculator = Calculator@1e64cc4d
scala> calc.color
res0: String = black
表达式
 上文的Calculator例子说明了Scala是如何面向表达式的。颜色的值就是绑定在一个if/else表达式上的。Scala是高度面向表达式的:大多数东西都是表达式而非指令。
 
 旁白: 函数 vs 方法
 函数和方法在很大程度上是可以互换的。由于函数和方法是如此的相似,你可能都不知道你调用的东西是一个函数还是一个方法。而当真正碰到的方法和函数之间的差异的时候,你可能会感到困惑。
 
 当你可以调用一个不带括号的“函数”,但是对另一个却必须加上括号的时候,你可能会想哎呀,我还以为自己知道Scala是怎么工作的呢。也许他们有时需要括号?你可能以为自己用的是函数,但实际使用的是方法。复制代码scala> class C {
     |   var acc = 0
     |   def minc = { acc += 1 }
     |   val finc = { () => acc += 1 }
     | }
defined class C
scala> val c = new C
c: C = C@1af1bd6
scala> c.minc // calls c.minc()
scala> c.finc // returns the function as a value:
res2: () => Unit = <function0>
在实践中,即使不理解方法和函数上的区别,你也可以用Scala做伟大的事情。如果你是Scala新手,而且在读两者的差异解释,你可能会跟不上。不过这并不意味着你在使用Scala上有麻烦。它只是意味着函数和方法之间的差异是很微妙的,只有深入语言内部才能清楚理解它。
 
 继承
 
 参考 Effective Scala 指出如果子类与父类实际上没有区别,类型别名是优于继承的。A Tour of Scala 详细介绍了子类化。复制代码class ScientificCalculator(brand: String) extends Calculator(brand) {
  def log(m: Double, base: Double) = math.log(m) / math.log(base)
}
 重载方法
 
 复制代码class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
  def log(m: Int): Double = log(m, math.exp(1))
}
抽象类
 你可以定义一个抽象类,它定义了一些方法但没有实现它们。取而代之是由扩展抽象类的子类定义这些方法。你不能创建抽象类的实例。
 
 复制代码scala> abstract class Shape {
     |   def getArea():Int    // subclass should define this
     | }
defined class Shape
scala> class Circle(r: Int) extends Shape {
     |   def getArea():Int = { r * r * 3 }
     | }
defined class Circle
scala> val s = new Shape
<console>:8: error: class Shape is abstract; cannot be instantiated
       val s = new Shape
               ^
scala> val c = new Circle(2)
c: Circle = Circle@65c0035b
特质(Traits)
 特质是一些字段和行为的集合,可以扩展或混入(mixin)你的类中。
 
 复制代码trait Car {
  val brand: String
}
trait Shiny {
  val shineRefraction: Int
}
通过with关键字,一个类可以扩展多个特质:复制代码class BMW extends Car {
  val brand = "BMW"
}
 参考 Effective Scala 对特质的观点。复制代码class BMW extends Car with Shiny {
  val brand = "BMW"
  val shineRefraction = 12
}
什么时候应该使用特质而不是抽象类? 如果你想定义一个类似接口的类型,你可能会在特质和抽象类之间难以取舍。这两种形式都可以让你定义一个类型的一些行为,并要求继承者定义一些其他行为。一些经验法则:
 
 你不是问这个问题的第一人。可以查看更全面的答案: stackoverflow: Scala特质 vs 抽象类 , 抽象类和特质的区别, andScala编程: 用特质,还是不用特质?优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。例如,你不能说trait t(i: Int) {},参数i是非法的。
 
 
 类型
 此前,我们定义了一个函数的参数为Int,表示输入是一个数字类型。其实函数也可以是泛型的,来适用于所有类型。当这种情况发生时,你会看到用方括号语法引入的类型参数。下面的例子展示了一个使用泛型键和值的缓存。
 
 方法也可以引入类型参数。复制代码trait Cache[K, V] {
  def get(key: K): V
  def put(key: K, value: V)
  def delete(key: K)
}
 apply 方法
 当类或对象有一个主要用途的时候,apply方法为你提供了一个很好的语法糖。
 
 或复制代码scala> class Foo {}
defined class Foo
scala> object FooMaker {
     |   def apply() = new Foo
     | }
defined module FooMaker
scala> val newFoo = FooMaker()
newFoo: Foo = Foo@5b83f762
 在这里,我们实例化对象看起来像是在调用一个方法。以后会有更多介绍!复制代码scala> class Bar {
     |   def apply() = 0
     | }
defined class Bar
scala> val bar = new Bar
bar: Bar = Bar@47711479
scala> bar()
res8: Int = 0
 单例对象
 单例对象用于持有一个类的唯一实例。通常用于工厂模式。
 
 可以这样使用:复制代码object Timer {
  var count = 0
  def currentCount(): Long = {
    count += 1
    count
  }
}
 单例对象可以和类具有相同的名称,此时该对象也被称为“伴生对象”。我们通常将伴生对象作为工厂使用。复制代码scala> Timer.currentCount()
res0: Long = 1
下面是一个简单的例子,可以不需要使用’new’来创建一个实例了。
 
 复制代码class Bar(foo: String)
object Bar {
  def apply(foo: String) = new Bar(foo)
}
函数即对象
 在Scala中,我们经常谈论对象的函数式编程。这是什么意思?到底什么是函数呢?
 函数是一些特质的集合。具体来说,具有一个参数的函数是Function1特质的一个实例。这个特征定义了apply()语法糖,让你调用一个对象时就像你在调用一个函数。
 
 这个Function特质集合下标从0开始一直到22。为什么是22?这是一个主观的魔幻数字(magic number)。我从来没有使用过多于22个参数的函数,所以这个数字似乎是合理的。复制代码scala> object addOne extends Function1[Int, Int] {
     |   def apply(m: Int): Int = m + 1
     | }
defined module addOne
scala> addOne(1)
res2: Int = 2
apply语法糖有助于统一对象和函数式编程的二重性。你可以传递类,并把它们当做函数使用,而函数本质上是类的实例。
 这是否意味着,当你在类中定义一个方法时,得到的实际上是一个Function*的实例?不是的,在类中定义的方法是方法而不是函数。在repl中独立定义的方法是Function*的实例。
 类也可以扩展Function,这些类的实例可以使用()调用。
 
 可以使用更直观快捷的extends (Int => Int)代替extends Function1[Int, Int]复制代码scala> class AddOne extends Function1[Int, Int] {
     |   def apply(m: Int): Int = m + 1
     | }
defined class AddOne
scala> val plusOne = new AddOne()
plusOne: AddOne = <function1>
scala> plusOne(1)
res0: Int = 2
 复制代码class AddOne extends (Int => Int) {
  def apply(m: Int): Int = m + 1
}
包
 你可以将代码组织在包里。
 
 在文件头部定义包,会将文件中所有的代码声明在那个包中。复制代码package com.twitter.example
值和函数不能在类或单例对象之外定义。单例对象是组织静态函数(static function)的有效工具。
 
 现在你可以直接访问这些成员复制代码package com.twitter.example
object colorHolder {
  val BLUE = "Blue"
  val RED = "Red"
}
 注意在你定义这个对象时Scala解释器的返回:复制代码println("the color is: " + com.twitter.example.colorHolder.BLUE)
 这暗示了Scala的设计者是把对象作为Scala的模块系统的一部分进行设计的。复制代码scala> object colorHolder {
     |   val Blue = "Blue"
     |   val Red = "Red"
     | }
defined module colorHolder
 模式匹配
 这是Scala中最有用的部分之一。
 匹配值
 
 使用守卫进行匹配复制代码val times = 1
times match {
  case 1 => "one"
  case 2 => "two"
  case _ => "some other number"
}
 注意我们是怎样将值赋给变量’i’的。复制代码times match {
  case i if i == 1 => "one"
  case i if i == 2 => "two"
  case _ => "some other number"
}
在最后一行指令中的_是一个通配符;它保证了我们可以处理所有的情况。
 否则当传进一个不能被匹配的数字的时候,你将获得一个运行时错误。我们以后会继续讨论这个话题的。
 
 参考 Effective Scala 对什么时候使用模式匹配 和 模式匹配格式化的建议. A Tour of Scala 也描述了 模式匹配
 
 匹配类型
 你可以使用 match来分别处理不同类型的值。
 
 复制代码def bigger(o: Any): Any = {
  o match {
    case i: Int if i < 0 => i - 1
    case i: Int => i + 1
    case d: Double if d < 0.0 => d - 0.1
    case d: Double => d + 0.1
    case text: String => text + "s"
  }
}
匹配类成员
 还记得我们之前的计算器吗。
 让我们通过类型对它们进行分类。
 一开始会很痛苦。
 
 (⊙o⊙)哦,太痛苦了。幸好Scala提供了一些应对这种情况的有效工具。复制代码def calcType(calc: Calculator) = calc match {
  case _ if calc.brand == "hp" && calc.model == "20B" => "financial"
  case _ if calc.brand == "hp" && calc.model == "48G" => "scientific"
  case _ if calc.brand == "hp" && calc.model == "30B" => "business"
  case _ => "unknown"
}
 样本类 Case Classes
 使用样本类可以方便得存储和匹配类的内容。你不用new关键字就可以创建它们。
 
 样本类基于构造函数的参数,自动地实现了相等性和易读的toString方法。复制代码scala> case class Calculator(brand: String, model: String)
defined class Calculator
scala> val hp20b = Calculator("hp", "20b")
hp20b: Calculator = Calculator(hp,20b)
 样本类也可以像普通类那样拥有方法。复制代码scala> val hp20b = Calculator("hp", "20b")
hp20b: Calculator = Calculator(hp,20b)
scala> val hp20B = Calculator("hp", "20b")
hp20B: Calculator = Calculator(hp,20b)
scala> hp20b == hp20B
res6: Boolean = true
 使用样本类进行模式匹配
 case classes are designed to be used with pattern matching. Let’s simplify our calculator classifier example from earlier.
 样本类就是被设计用在模式匹配中的。让我们简化之前的计算器分类器的例子。
 
 最后一句也可以这样写复制代码val hp20b = Calculator("hp", "20B")
val hp30b = Calculator("hp", "30B")
def calcType(calc: Calculator) = calc match {
  case Calculator("hp", "20B") => "financial"
  case Calculator("hp", "48G") => "scientific"
  case Calculator("hp", "30B") => "business"
  case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel)
}
 或者我们完全可以不将匹配对象指定为Calculator类型复制代码case Calculator(_, _) => "Calculator of unknown type"
 或者我们也可以将匹配的值重新命名。复制代码case _ => "Calculator of unknown type"
 复制代码case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
异常
 Scala中的异常可以在try-catch-finally语法中通过模式匹配使用。
 
 try也是面向表达式的复制代码try {
  remoteCalculatorService.add(1, 2)
} catch {
  case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailable. should have kept your trusty HP.")
} finally {
  remoteCalculatorService.close()
}
 这并不是一个完美编程风格的展示,而只是一个例子,用来说明try-catch-finally和Scala中其他大部分事物一样是表达式。复制代码val result: Int = try {
  remoteCalculatorService.add(1, 2)
} catch {
  case e: ServerIsDownException => {
    log.error(e, "the remote calculator service is unavailable. should have kept your trusty HP.")
    0
  }
} finally {
  remoteCalculatorService.close()
}
当一个异常被捕获处理了,finally块将被调用;它不是表达式的一部分。
 
 Built at @twitter by @stevej, @marius, and @lahosken with much help from @evanm, @sprsquish, @kevino, @zuercher, @timtrueman, @wickman, and @mccv; Russian translation by appigram; Chinese simple translation by jasonqu; Korean translation by enshahar;
 
 Licensed under the Apache License v2.0.
 
 |