apply 方法
当类或对象有一个主要用途的时候,apply
方法为你提供了一个很好的语法糖。
1 | scala> class Foo {} |
或
1 | scala> class Bar { |
在这里,我们实例化对象看起来像是在调用一个方法。以后会有更多介绍!
单例对象 (object)
单例对象用于持有一个类的唯一实例。通常用于 工厂模式。
1 | object Timer { |
可以这样使用:
1 | scala> Timer.currentCount() |
单例对象可以和类具有相同的名称,此时该对象也被称为伴生对象
。我们通常将伴生对象作为工厂使用。
下面是一个简单的例子,可以不需要使用 new
来创建一个实例了。
1 | class Bar(foo: String) |
函数即对象
Scala 中,我们经常谈论对象的函数式编程。这是什么意思?到底什么是函数呢?
函数是一些特质的集合。具体来说,具有一个参数的函数是 Function1 trait 的一个实例。这个 trait 定义了 apply
语法糖,让你调用一个对象时就像你在调用一个函数。(关于 Function1 trait 在 Scala API doc 中有详细介绍 https://www.scala-lang.org/api/2.7.5/scala/Function1.html )
1 | scala> object addOne extends Function1[Int, Int] { |
这个 Function trait 集合下标从 0 开始一直到 22。为什么是 22?这是一个主观的魔幻数字 (magic number)。我从来没有使用过多于 22 个参数的函数,所以这个数字似乎是合理的。(在 Scala )
apply
语法糖有助于统一对象和函数式编程的二重性。你可以传递类,并把它们当做函数使用,而函数本质上是类的实例。
这是否意味着,当你在类中定义一个方法时,得到的实际上是一个 Function *的实例?不是的,在类中定义的方法是方法而不是函数。在 repl 中独立定义的方法是 Function *的实例。
类也可以扩展 Function,这些类的实例可以使用 ()
调用。
1 | scala> class AddOne extends Function1[Int, Int] { |
可以使用更直观快捷的 extends (Int => Int)
代替 extends Function1[Int, Int]
1 | class AddOne extends (Int => Int) { |
包 (package)
你可以将代码组织在包里。
1 | package com.twitter.example |
在文件头部定义包,会将文件中所有的代码声明在那个包中。
值和函数不能在类或单例对象之外定义。单例对象是组织静态函数 static function
的有效工具。
1 | package com.twitter.example |
现在你可以直接访问这些成员
1 | println("the color is: " + com.twitter.example.colorHolder.BLUE) |
注意在你定义这个对象时 Scala 解释器的返回:
1 | scala> object colorHolder { |
这暗示了 Scala 的设计者是把对象作为 Scala 的模块系统的一部分进行设计的。
模式匹配
这是 Scala 中最有用的部分之一。
匹配值
1 | val times = 1 |
使用守卫进行匹配
1 | times match { |
注意我们是怎样获取变量 i
的值的。
在最后一行指令中的_
是一个通配符;它保证了我们可以处理所有的情况。 (注意第一章提到的 _
在不同上下文中有不同含义)
否则当传进一个不能被匹配的数字的时候,你将获得一个运行时错误。我们以后会继续讨论这个话题的。
参考 Effective Scala 对什么时候使用模式匹配和模式匹配格式化的建议。A Tour of Scala 也描述了模式匹配
匹配类型
你可以使用 match
来分别处理不同类型的值。
1 | def bigger(o: Any): Any = { |
匹配类成员
还记得我们之前的计算器吗。
让我们通过类型对它们进行分类。
一开始会很痛苦。
1 | def calcType(calc: Calculator) = calc match { |
(⊙o⊙) 哦,太痛苦了。幸好 Scala 提供了一些应对这种情况的有效工具。
样本类 (Case Classes)
使用样本类可以方便提前准备好需要进行匹配类内容,并且不用 new 关键字就可以创建它们。
1 | scala> case class Calculator(brand: String, model: String) |
样本类基于构造函数的参数,自动地实现了相等性和易读的 toString 方法。
1 | scala> val hp20b = Calculator("HP", "20b") |
样本类也可以像普通类那样拥有方法。
使用样本类进行模式匹配
样本类就是被设计用在模式匹配中的。让我们简化之前的计算器分类器的例子。
1 | val hp20b = Calculator("HP", "20B") |
最后一句也可以这样写
1 | case Calculator(_, _) => "Calculator of unknown type" |
或者我们完全可以不将匹配对象指定为 Calculator 类型
1 | case _ => "Calculator of unknown type" |
或者我们也可以将匹配的值重新命名。
1 | case c (_, _) => "Calculator: %s of unknown type".format(c) |
异常
Scala 中的异常可以在 try-catch-finally
语法中通过模式匹配使用。
1 | try { |
这并不是一个完美编程风格的展示,而只是一个例子,用来说明 try-catch-finally
和 Scala 中其他大部分事物一样是表达式。
当一个异常被捕获处理了,finally
块将被调用;它不是表达式的一部分。