yunfenglee 2020-02-18
类,字段和方法
在scala里定义一个典型的类,代码如下:
class ChecksumAccumulator { private var sum = 0 def add(b: Byte): Unit = { sum += b } def checksum(): Int = { return ~(sum & 0xFF) + 1 } }
1.在 Scala 里成员公开的方法是不显式地指定任何访问修饰符。换句话说,你在 Java 里要写上 “public”的地方,在 Scala 里只要什么都不要写就成。Public 是 Scala 的缺省访问级别。
2.Scala里方法参数的一个重要特征是它们都是val,不是var。如果你想在方法里面给参数重新赋值,结果是编译失败:
def add(b: Byte): Unit = { // b += 1 sum += b }
3.如果没有发现任何显式的返回语句,Scala 方法将返回方法中最后一个计算得到的值。假如某个方法仅计算单个结果表达式,则可以去掉大括号。如果结果表达式很短,甚至可 以把它放在 def 同一行里。
所以上面的checksum方法可以改写如下:
def checksum(): Int = ~(sum & 0xFF) + 1
4.当你去掉方法体前面的等号时,它的结果类型将注定是 Unit。 不论方法体里面包含什么都不例外,因为 Scala 编译器可以把任何类型转换为 Unit。另外,如果方法的最后结果是 String,但方法的结果类型被声明为 Unit,那么 String 将被转变 为 Unit 并失去它的值。
def checksum2() {~(sum & 0xFF) + 1} def checksum3():Unit = "hello";
Singleton对象
Scala 比 Java 更面向对象的一个方面是 Scala 没有静态成员。替代品是,Scala 有单例对象:singleton object
创建ChecksumAccumulator类的“伴生对象”:
// 文件 ChecksumAccumulator.scala import scala.collection.mutable.Map object ChecksumAccumulator { private val cache = Map[String, Int]() def calculate(s: String): Int = if (cache.contains(s)) cache(s) else { val acc = new ChecksumAccumulator for (c <- s) acc.add(c.toByte) val cs = acc.checksum() cache += (s -> cs) cs } }
表中的单例对象被叫做 ChecksumAccumulator,与前一个例子里的类同名。当单例对象与 某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在 同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
单例对象扩展了超类并可以混入特质。由于每个单例对象都是超类的实例并混入了特质,你可以通过这些类型调用它的方法,用这些类型的变量指代它,并把它传递给需要这些类型的方法。
类和单例对象间的一个差别是,单例对象不带参数,而类可以。因为你不能用new关键字 实例化一个单例对象,你没机会传递给它参数。每个单例对象都被作为由一个静态变量指向的虚构类:synthetic class的一个实例来实现,因此它们与Java静态类有着相同的初始 化语法。4 4.4 Scala 程序 特别要指出的是,单例对象会在第一次被访问的时候初始化。
不与伴生类共享名称的单例对象被称为孤立对象:standalone object。由于很多种原因你 会用到它,包括把相关的功能方法收集在一起,或定义一个 Scala 应用的入口点。
Scala 程序
要执行 Scala 程序,你一定要提供一个有 main 方法(仅带一个参数,Array[String],且 结果类型为 Unit)的孤立单例对象名。任何拥有合适签名的 main 方法的单例对象都可以 用来作为程序的入口点。
注意 Scala 隐式引用了包 java.lang 和 scala 的成员,和名为 Predef 的单例对象的成员,到每个 Scala 源文件中。Predef,被放置在包 scala 中,包含了许多有用的方法。例如,当在 Scala 源文件中写 pringln 的时候,你实际调用了 Predef 的 println。(Predef.pringln 运转并调用 Console.println,做实际的工 作。)当你写 assert,你是在调用 Predef.assert。
接下来,我们在main方法中完成对上述ChecksumAccumulator类和单列对象的调用,代码如下:
object Summer { def main(args: Array[String]) { val args2 = List("hello","hi"); for (arg <- args2) println(arg + ": " + calculate(arg)) } }
程序的运行结果如下:
hello: -20
hi: -209