包引用
所有Scala类(class)和对象(object)都必须放置在软件包中,而软件包必须在Scala源代码文件的开头声明和命名。下面的代码声明了Claim Check样本的源代码文件位于claimcheck软件包中
package co.vaughnvernon.reactiveenterprise.claimcheck
如果你想使用其他源代码文件或软件包中定义的类、对象和其他Scala数据类型,就必须使用import 语句引用它们。Scala语言的import语句与Java的import 语句非常相似。
import akka.actor.Actor
import akka.actor.Props
//或者
import akka.actor
类声明
应该在Scala源代码文件中添加什么内容呢?首先,就像Java和C#支持类一样,Scala语言也支持类并对该功能做了一点提高。
class ItemChecker{}
abstract class ItemContainer{}
class ShoppingCart extends ItemContainer{}
第一个定义的类(Itemchecker)不会扩展任何特定的基类。另一方面,shoppingCart 扩展了抽象基类Itemcontainer。此处没有介绍这些类的细节。Itemchecker和shoppingcart类都具有的特点之一是,都拥有无参数构造器。这意味着可以使用下面的方式实例化shoppingcart类:
val shoppingcart=new ShoppingCart()
变量声明
如果shoppingcart是一个var变量,那么完全可以对其进行反复赋值。
var shoppingCart=new ShoppingCart
val differentCart=new ShoppingCartshoppingcart=differentcart//该表达式有效
注意,在这两个shoppingcart变量声明语句中,并没有确切地指定数据类型,仅指定了引用。这是因为可以通过Scala语言的类型推断功能解决这些麻烦。类型推断是指Scala编译器能够分析代码,并检测出隐含的数据类型。该功能得出的结果与以明确方式声明类型的作用相同。
val shoppingCart:ShoppingCart=new ShoppingCart
构造器
这里展示了为shoppingcart类添加构造器参数的方式(使用Scala 语言调用类参数的方式):
class ShoppingCart(val maximumItems:Int)extends ItemContainer{}
伴生对象
伴生对象是Scala语言中的一个特殊概念,而且它永远都会是单例对象;换言之,程序中仅会存在一个该类型的对象。在本例中,Defaultcatalog是一个类,而它的伴生对象就像一个工厂,用于获取Defaultcatalog实例。当然,伴生对象还能够完成其他任务。
下面是一段简单的实现代码:
object DefaultCatalog{
def apply=new DefaultCatalog(catalogConfig)
def name=catalogConfig.name
}
当你使用Defaultcatalog()表达式时,apply方法就会被调用,如上面的例子所示。apply方法仅会返回一个DefaultCatalog类的新实例,而不会要求客户端必须了解catalogconfig参数的情况。可以在伴生对象中定义任何种类的方法(如name)。为了使用该方法,客户端会通过下面的方式使用伴生对象:
val catalogName=DefaultCatalog.name
这种方式没有问题,但使用伴生对象的动机是什么呢?Scala不支持静态方法,因此伴生对象提供了对其伴生类所需的各种工厂和实用方法的支持。
object DefaultCatalog{
def apply:Catalog={
new DefaultCatalog(catalogConfig)
def name:String={}
}
}
catalogConfig.name不仅可以使用花括号封装代码块,还可以通过在引用后面添加:type,声明方法会返回哪种类型的数据。明确声明方法返回数据的类型是最好的Scala编程习惯。app1y引用的代码块会返回Catalog类型的实例,而name引用的代码块会返回string类型的实例。
操作符
如果你使用过Java,可能会因无法将符号用作方法的名称而感到有一点遗憾。Scala改变了这一点,在Scala中使用操作符甚至比在C++中更方便。当然,不应滥用这个功能。没有限制,自由也不会存在。例如,在Scala中可以将!用作方法的名称。
def!(message:Any):Unit={}
实际上,下面是一种发送Akka消息的方式:
actor!SomeMessage()
使用!方法的方式与使用te11()方法的方式相同。而且,还不需要在接收者actor对象后面加圆点,也不需要使用括号将参数SomeMessage()括起来,这使代码更易于阅读得多。
特征(接口)
Scala源代码文件中还可以定义特征(trait)。Scala中的特征有点像Java和C#中的接口和抽象类。它像一种接口,会在内部定义为使用它们的类的对象定义协议。它还与抽象类相似,因为特征内部至少会有一部分拥有默认的实现代码。从这方面看,特征就是一种抽象类。如果特征被使用了,那么它就会变成抽象类。事实上,多重特征可以由单个类实现或扩展。该操作通常称为混入多重特征。
class ShoppingCart(
val catalogSource:CatalogSource,val maximumItems:Int)
extends ItemBrowser with ItemContainer{}
样本类
前面介绍过Scala类的种类与Java和C#中类的种类相似。除了那些相似的类外,Scala还有一种样本类(case class)。
case class ProcessOrder(orderId:String)
val message=ProcessOrder("123")
循环和迭代
此刻你可能想知道Scala是怎样支持循环和迭代操作的。实际上,Scala提供了非常多的循环方式。本书仅会列举几个示例。下面是编写for循环的一种方式:
for(counter <-1 to 20){
print1n(counter)//变量counter每次都被赋予1至20之间的新值
使用for表达式可以轻松迭代集合。
for(element <-Vector(1,2,3,4,5)){
print1n(element)//element变量每次都被赋予1至5之间的新值
for(element <-Vector(1,2,3,4,5)){
print1n(element)//element变量每次都被赋予1至5之间的新值
你可以使用集合本身提供的迭代操作,还可以提供处理每个集合元素的闭包。
vector(1,2,3,4,5)map{element =>println(element)}
下面的示例使用了for推导语句:
val numbers=Vector(1,2,3,4,5,6,7,8,9,10)
val evenNumbers=for(number<-numbers){
if(number%2==0)
}yield number
还可以通过另一种方式获得相同的结果,即使用更为简洁的Scala代码:
val evenNumbers=
for{
number<-numbers if number%2==0
}yield number
泛型
如果你想完整地声明evenNumbers引用,可使用下面的方式:
val evenNumbers:Vector[Int]=..…
这段代码声明了一个类型为Int的Vector集合。Java和C#使用尖括号表示泛型(即
模式匹配
模式匹配功能是Scala中功能最强大的工具之一,可以在多种环境(如由数字元素组成的集合中)中使用它。
Vector(1,2,3)map{
case 1=>print1n("One")//显式编号为1的单词
case 2=>print1n("Two")//显式编号为2的单词
case 3=>print1n("Three")//显式编号为3的单词
执行匹配操作时不必对集合进行迭代。对单个对象也可以进行模式匹配,在下面的例子中该单个对象为10。
10 match{
case 1=>println("One")
case 2=>println("Two")
case 3=>println("Three")
case =>print1n("Several")//显示默认的匹配单词
}
类型匹配
case class ConfigureProcessor(orderProvider: OrderProvider, timeOut: Long)
case class ProcessOrder(orderId: String)
val message=ProcessOrder("123")
message match{
case config: ConfigureProcessor=> configureProcessor(init)
case processOrder: ProcessOrder=>
val order=orderFor(processOrder. orderId)
...
}
类型与参数匹配
message match{
case ConfiqureProcessor(orderProvider,timeOut)=>
configureProcessor(orderProvider,timeOut)
case ProcessOrder(orderId)=>
val order=orderFor(orderId)
...
}