Scala编程第3版第四章(类和对象)
2020-02-17 00:11:44    38    0    0
yuziyue

 类、字段、方法

在类中,通过 val 或者 var 定义的字段是指向对象的变量,def 定义的方法包含可执行代码。标记私有防止外部直接访问字段:private var sum = 0  。在 Scala 中 默认就是public的,比如ChecksumAccumulator.scala

class ChecksumAccumulator {
    private var sum = 0
    def add(b: Byte): Unit = {sum += b}
    def checksum(): Int = sum * 10
}

单例对象

Scala 比 Java 更面向对象的一点,是Scala的类不允许有静态成员。对此类使用场景,Scala提供了单例对象。单例对象的定义跟类很像,只不过class关键字被换成了object关键字。 

 

当单例对象的跟某个类共同用一个名字时,它被称作这个类的伴生对象,必须在同一个源文件中定义类和类的伴生对象,同时,这个类又叫做这个单例对象的伴生类。类和它的伴生对象可以相互访问对方的私有成员。

 

如果你是Java程序员,可以把单例对象当作是用于安置那些用Java时打算编写的静态方法。可以用类似的方式来访问单例对象的方法:单例对象名、英文句点和方法名。不过,单例对象并不仅仅是用来存放静态方法,它是一等的对象,可以把单例对象的名称想象成附加在对象身上的"名字标签",比如 ChecksumAccumulator.scala

import scala.collection.mutable

object ChecksumAccumulator {
    private val cache = mutable.Map.empty[String, Int]
    def caclulate(s: String): Int = {
        if (cache.contains(s))
            cache(s)
        else
            val single = new ChecksumAccumulator
            for (c <- s) single.add(c)
            val cs = single.checksum()
            cache += (s -> cs)
            cs
    }
}

单例对象不接受参数,类可以接受参数。单例对象从初始化的语义上和Java的静态成员是一致的,尤其体现在,单例对象在有代码首次访问时才被初始化。

 

没有同名的伴生类的单例对象称为孤立对象(standalone object),孤立对象有很多种用途,包括将工具方法归集在一起,或定义Scala应用程序的入口等。

 

Scala应用程序

要运行一个Scala程序,必须提供一个独立对象的名称。这个独立对象需要包含一个main方法,该方法接收一个Array[String]作为参数,结果类型为Unit。任何带有满足正确签名的main方法的独立对象都能被用作应用程序的入口。比如 Summer.scala

import ChecksumAccumulator.calculate

object Summer {
    def main(args: Array[String]) = {
        for (arg <- args)
            println(arg + ": " + calculate(arg))
    }
}

 

Scala在每一个Scala源码文件都隐式地引入了java.lang和scala包的成员,以及名为Predef的单例对象的所有成员。位于scala包的P r e d e f包含了很多有用的方法。比如,当你在Scala源码中使用println时,实际上调用了Predef的println(Predef. println转而调用Console.println,执行具体的操作)。而当你写下assert时,实际上是调用了Predef.assert。

 

Scala和Java的区别之一,是Java要求你将公共的类放入跟类同名的文件中(例如需要将SpeedRacer类放到SpeedRacer.java中),而在Scala中可以任意命名.scala文件,不论你放什么类或代码到这个文件中。不过,通常对于那些非脚本的场景,把类放入以类名命名的文件是推荐的做法,就像Java那样,以便程序员能够更容易地根据类名定位到对应的文件。这也是我们在命名Summer.scala和ChecksumAccumulator.scala时所采取的策略。

 

ChecksumAccumulator.scala和Summer.scala都不是脚本,因为它们都是以定义结尾的。而脚本则不同,必须以一个可以计算出结果的表达式结尾。因此,如果你尝试以脚本的方式运行Summer.scala,解释器会报错,提示你Summer.scala并不以一个结果表达式结尾(当然这是假定你并没有在Summer对象定义之后自己再添加任何额外的表达式)。需要用Scala编译器实际编译这些文件,然后运行编译出来的类。编译的方式之一,是使用scalac这个基础的Scala编译器,就像这样:

 

这将编译你的源文件,不过在编译结束之前,你可能会注意到一个比较明显的延迟。这是因为每一次编译器启动,它都会花时间扫描jar文件的内容以及执行其他一些初始化工作,然后才开始关注你提交给它的新的源码文件。因为这个原因,Scala的分发包还包含了一个名为fsc的Scala编译器的守护进程(daemon)。使用的方式如下:

第一次运行fsc,它会创建一个本地的服务器守护进程,绑定到计算机的某个端口上。然后,它会通过这个端口将需要编译的文件发送给这个守护进程。下一次运行fsc的时候,这个守护进程已经在运行了,所以fsc会简单地将文件清单发给这个守护进程,然后守护进程就会立即编译这些文件。使用fsc,只有在首次运行时才需要等待Java运行时启动。如果你想要停止f s c这个守护进程,可以执行fsc -shutdown。

 

不论是运行scalac还是fsc命令,都会产出Java类文件,这些类文件可以用scala命令来运行:

scala Summer of love


App特质

要编写一个可以运行的程序必须要编写一个object,并且在里面编写main方法。Scala为了节省步骤,给我们定义了App特质,我们只需要这样做即可。将下面代码保存为 Echo.scala

object Echo extends App {
    for (arg <- args)
        println(arg)
}
$ scala Echo.scala  Hello World!
Hello
World!

 

上一篇: Windows命令行配置IP地址

下一篇: Scala编程第3版第二章(入门)

38 人读过
文档导航