当类或对象有一个主要用途的时候,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()语法糖,让你调用一个对象时就像你在调用一个函数。
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,这些类的实例可以使用()调用。
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 |
extends (Int => Int)
代替 extends Function1[Int, Int]
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" |
} |
println("the color is: " + com.twitter.example.colorHolder.BLUE) |
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" |
} |
times match { |
case i if i == 1 => "one" |
case i if i == 2 => "two" |
case _ => "some other number" |
} |
i
的。
在最后一行指令中的_是一个通配符;它保证了我们可以处理所有的情况。
否则当传进一个不能被匹配的数字的时候,你将获得一个运行时错误。我们以后会继续讨论这个话题的。
参考 Effective Scala 对[什么时候使用模式匹配](http://twitter.github.com/effectivescala/#Functionalprogramming-Pattern matching)和[模式匹配格式化的建议](http://twitter.github.com/effectivescala/#Formatting-Pattern matching)。 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" |
} |
} |
还记得我们之前的计算器吗。
让我们通过类型对它们进行分类。
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" |
} |
使用样本类可以方便得存储和匹配类的内容。你不用 new 关键字就可以创建它们。
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 |
样本类就是被设计用在模式匹配中的。让我们简化之前的计算器分类器的例子。
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) |
} |
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-catch-finally 和 Scala 中其他大部分事物一样是表达式。
当一个异常被捕获处理了,finally 块将被调用;它不是表达式的一部分。