Scala学习(六)---Scala对象


Scala中的对象

摘要:

在本篇中,你将会学到何时使用Scala的object语法结构。在你需要某个类的单个实例时,或者想为其他值或函数找一个可以挂靠的地方时,你就会用到它。本篇的要点包括:

1. 用对象作为单例或存放工具方法

2. 类可以拥有—个同名的伴生对象

3. 对象可以扩展类或特质

4. 对象的apply方法通常用来构造伴生类的新实例

5. 如果不想显式定义main方法,可以用扩展App特质的对象

6. 你可以通过扩展Enumeration对象来实现枚举


单例对象

Scala没有静态方法或静态字段,你可以用object这个语法结构来达到同样目的。对象定义了某个类的单个实例,包含了你想要的特性。例如:

object Accounts {

private var lastNumber=0

def newUniqueNumber() = { lastNumber+=1, lastNumber}

}

当你在应用程序中需要一个新的唯一账号时,调用Accounts.newUniqueNumber()即可。对象的构造器在该对象第一次被使用时调用。在本例中,Accounts的构造器在Accounts.newUniqueNumber()的首次调用时执行。如果一个对象从未被使用,那么其构造器也不会被执行。

对象本质上可以拥有类的所有特性,它甚至可以扩展其他类或特质。只有一个例外:你不能提供构造器参数。对于任何你在Java或C++中会使用单例对象的地方,在Scala中都可以用对象来实现:

■ 作为存放工具函数或常量的地方

■ 高效地共享单个不可变实例

■ 需要用单个实例来协调某个服务时,可参考单例模式

注意:很多人都看低单例模式。Scala提供的是工具,可以做出好的设计,也可以做出糟糕的设计,你需要做出自己的判断。


伴生对象

在Java或C++中,你通常会用到既有实例方法又有静态方法的类。在Scala中,你可以通过类和与类同名的"伴生"对象来达到同样的目的。例如:

class Account {

val id=Account.newUniqueNumber()

private var balance =0

def deposit (amount: Double) { balance+=amount }

}

object Account{ // 伴生对象

private var lastNumber=0

private def newUniqueNumber() = { lastNumber+=1;lstNumber }

}

类和它的伴生对象可以相互访问私有特性。它们必须存在于同一个源文件中。这说明了类的伴生对象可以被访问,但并不在作用域当中。举例来说,Account类必须通过Account.newUniqueNumber()而不是直接用newUniqueNumber()来调用伴生对象的方法。


扩展类或特质的对象

一个object可以扩展类以及一个或多个特质,其结果是一个扩展了指定类以及特质的类的对象,同时拥有在对象定义中给出的所有特性。一个有用的使用场景是给出可被共享的缺省对象。举例来说,考虑在程序中引入一个可撤销动作的类:

abstract class UndoableAction (val description: String) {

def undo() : Unit

def redo() : Unit

}

默认情况下可以是"什么都不做"。当然了,对于这个行为我们只需要一个实例即可:

object DoNothingAction extends UndoableAction("Do nothing") {

override def undo () {}

override def redo () {}

}

DoNothingAction对象可以被所有需要这个缺省行为的地方共用。

val actions=Map( "open" -> DoNothingAction,"save" -> DoNothingAction,…) // 打开和保存功能尚未实


apply方法

apply含义

我们通常会定义和使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用:

Object(参数1,…,参数N)

通常,这样—个apply方法返回的是伴生类的对象。举例来说,Array对象定义了apply方法,让我们可以用下面这样的表达式来创建数组:

Array("Mary", "had", "a", "little", "lamb")

为什么不用构造器呢?对于嵌套表达式而言,省去new关键字会方便很多,例如:

Array (Array (1, 7),Array (2, 9))

注意:Array(100)和new Array(100)很容易搞混。前一个表达式调用的是apply(100),输出一个单元素整数100的Array[Int]。而第二个表达式调用的是构造器this(100),结果是Array[Nothing],包含了100个null元素。

apply示例

这里有一个定义apply方法的示例:

class Account private (val id: Int, initialBalance: Double) {

private var balance=initiaIBalance

………

}

object Account { //伴生对象

def apply (initialBalance: Double) =

new Account (newUniqueNumber(), initialBalance)

}

这样一来你就可以用如下代码来构造账号了:

val acct = Account (1000.0)


应用程序对象

main方法

每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String]=> Unit:

object Hello{

def main (args: Array[String]) {

println("Hello, World! ")

}

}

扩展App特质

除了每次都提供自己的main方法外,你也可以扩展App特质,然后将程序代码放人构造器方法体内:

object Hello extends App{

println("Hello, World! ")

}

命令行参数

如果你需要命令行参数,则可以通过args属性得到:

object Hello extends App{

if (args.length > 0)

println("Hello, "+args (0))

else

println("Hello, World! ")

}

时间特质

如果你在调用该应用程序时设置了scala.time选项的话,程序退出时会显示逝去的时间

scalac Hello.scala

scala -Dscala.time Hello Fred

Hello, Fred

[total 4ms]

所有这些涉及一些小小的魔法。App特质扩展自另一个特质Delayedlnit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedlnit方法中。App特质的main方法捕获到命令行参数,调用delayedlnit方法,并且还可以根据要求打印出逝去的时间。在较早版本的Scala有一个Application特质来达到同样的目的。那个特质是在静态初始化方法中执行程序动作,并不被即时编译器优化,因此应尽量使用新的App特质。


枚举

枚举定义初始化

和Java或C++不同,Scala并没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举。定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。例如:

object TrafficLightColor extends Enumeration (

val Red, Yellow,Green=Value

}

在这里我们定义了三个字段:Red、Yellow和Green,然后用Value调用将它们初始化。这是如下代码的简写:

val Red = Value

val Yellow = Value

val Green = Value

每次调用Value方法都返回内部类的新实例,该内部类也叫做Value。或者,你也可以向Value方法传人ID、名称,或两个参数都传:

val Red = Value (0, "Stop")

val Yellow = Value(10) // 名称为"Yellow"

val Green = Value("Go") // ID为11

如果不指定,则ID在将前一个枚举值基础上加一,从零开始,缺省名称为字段名。

枚举引用

定义完成后,你就可以用TrafficLightColor.Red、TrafficLightColor.Yellow等来引用枚举值了。如果这些变得冗长烦琐,则可以用如下语句直接引入枚举值:

import TrafficLightColor._

需要注意的是:枚举的类型是TrafficLightColor.Value而不是TrafficLightColor,后者是握有这些值的对象。有人推荐增加一个类型别名:

object TrafficLightColor extends Enumeration {

TrafficLightColor = Value

val Red, Yellow, Green=Value

}

现在枚举的类型变成了TraffcLightColor.TrafficLightColor,但仅当你使用import语句时这样做才显得有意义。例如:

import TrafficLightColor._

def doWhat (color : TrafficLightColor) = {

if (color==Red) "stop"

else if (color==Yellow) "hurry up"

else "go"

}

访问枚举

枚举值的ID可通过id方法返回,名称通过toString方法返回。对TrafficLightColor.values的调用输出所有枚举值的集:

for(c <- TrafficLightColor.values)

println (c.id+":"+c)

最后,你可以通过枚举的ID或名称来进行查找定位,以下两段代码都输出TrafficLightColor.Red对象:

TrafficLightColor (0) // 将调用Enumeration.apply

TrafficLightColor.withName( "Red")

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【Sunddenly】。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

时间: 2024-09-29 23:35:50

Scala学习(六)---Scala对象的相关文章

C#多线程学习(六) 互斥对象

C#多线程学习(六) 互斥对象 如何控制好多个线程相互之间的联系,不产生冲突和重复,这需要用到互斥对象,即:System.Threading 命名空间中的 Mutex 类. 我们可以把Mutex看作一个出租车,乘客看作线程.乘客首先等车,然后上车,最后下车.当一个乘客在车上时,其他乘客就只有等他下车以后才可以上车.而线程与Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待Mutex对象被释放,如果它等待的Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mute

scala学习02-类与对象,类型与操作

Scala类与对象,类型与操作 类的声明 class MyClass{ private var num = 0 // 如果想要不对外开放,使用private,如果不写,默认就是public //方法 //花括号前没有=号,这种情况下,返回值类型一定是Unit,也就是空,所以如果想有返回值,一定要加=号 def add(b:Byte){ num += b } //如果计算表达式只有一行,可以省略掉花括号 def add1(b:Byte):Unit = num += b //有返回值的正常方法: d

Scala学习(一)--Scala基础学习

Scala基础学习 摘要: 在篇主要内容:如何把Scala当做工业级的便携计算器使用,如何用Scala处理数字以及其他算术操作.在这个过程中,我们将介绍一系列重要的Scala概念和惯用法.同时你还将学到作为初学者如何浏览Scaladoc文档 1. 使用Scala解释器 2. 用var和val定义变量 3. 数值类型 4. 使用操作符和函数 5. 浏览Scaladoc Scala解释器 启动Scala解释器的步骤如下: 安装Scala 确保scala/bin目录位于系统PATH中 在你的操作系统中

scala学习-类与对象

类 / 对象 [<快学Scala>笔记] 一.类 1.Scala中的类是公有可见性的,且多个类可以包含在同一个源文件中: 1 class Counter{ 2 private var value = 0 //类成员变量必须初始化,否则报错 3 4 def increment(){ //类中的方法默认是公有可见性 5 value += 1 6 } 7 8 def current() = value //对于类中的“取值方法”,在定义时可省略掉括号,直接 def current = value 9

Scala学习 day01 Scala的类/方法/对象/单例对象

从今天开始,我将尽自己最大努力系统地学习Scala这门语言,希望自己能够坚持下去. Scala中的类 定义规则 class ClassName{     //类中的成员 } 在类定义里,可以放置字段和方法,这些被笼统地称之为成员

Scala学习(六)练习

Scala中的对象&练习 1. 编写一个Conversions对象,加入inchesToCentimeters,gallonsToLiters和milesToKilometers方法 程序代码: object Conversions {   private val i2c=30.48   private val g2l=3.785411784   private val m2k=1.609344   def inchesToCentimeters(inch:Double):Double={   

Scala学习笔记-伴生对象于孤立对象

Scala-伴生对象于孤立对象 Scala虽然是基于JVM构建的,但与Java还是有很多区别,其实一个重要的区别是Scala比Java更面向对象(纯面向对象),最明显的用例是scala中所有的操作符都是方法(操作符面向的使用者是对象). 伴生对象/孤立对象也是scala作为纯面向对象语言的一种体现. 孤立对象 先看一个例子 object Test{ var a = "helloworld" def helloworld(): Unit = { println("hellowo

Scala学习 -- 单例对象

Scala 比 Java 更面向对象一点,是 Scala 的类不允许静态(static)成员.对此类使用场景,Scala 提供了 单例对象(object).单例对象的定义看上去跟类定义很像,只不过 class 关键字被换成了 object 关键字. 在事例中单例对象名叫 ChecksumAccumulator, 跟前一个例子中的类名一样. 当单例对象跟某个类公用同一个名字时,它被称作这个类的 伴生对象(companion object). 必须在同一个源码文件中定义的类和类的伴生对象.同时,类又

Scala学习(五)---Scala中的类

Scala中的类 摘要: 在本篇中,你将会学习如何用Scala实现类.如果你了解Java或C++中的类,你不会觉得这有多难,并且你会很享受Scala更加精简的表示法带来的便利.本篇的要点包括: 1. 类中的字段自动带有getter方法和setter方法 2. 你可以用定制的getter/setter方法替换掉字段的定义,而不必修改使用类的客户端,这就是所谓的"统一访问原则" 3. 用@BeanProperty注解来生成JavaBeans的getXxx/setXxx()方法 4. 每个类