Scala伴生类和伴生对象

摘要:
与伴随类同名的单例对象称为孤立对象。举个例子:importscala.collection.mutable MapclassChecksumAccumulator{privatevarsum=0defaultadd{sum+=b}defchecksum():Int=~+1}objectChecksumAccucurator{Private valcache=Map[String,Int]()defcalculate:Int=ifcacheelse{valacc=newChecksumAccuculatorforacc.addvalcs=acchecksum(()cache+=printlncs}defmain{printlnprintlnprintln}}checksumAccumulatorsingleton对象有一个方法,calculate,用于计算字符串参数中字符的校验和。else子句的第一行定义了一个名为acc的val,并用新的ChecksumAccumulator实例初始化它。事实上,这里新增的实际上是ChecksumAccumulator单例对象的伴随类,即ChecksumAccucurator类。伴随类和伴随对象可以访问彼此的私有成员,因此acc可以访问ChecksumAccumulator单例对象的缓存变量。

单例对象与类同名时,这个单例对象被称为这个类的伴生对象,而这个类被称为这个单例对象的伴生类。伴生类和伴生对象要在同一个源文件中定义,伴生对象和伴生类可以互相访问其私有成员。不与伴生类同名的单例对象称为孤立对象。

看看例子:

import scala.collection.mutable.Map

class ChecksumAccumulator {
  private var sum = 0
  def add(b: Byte) {
    sum += b
  }
  def checksum(): Int = ~(sum & 0xFF) + 1
}

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)
      println("s:"+s+" cs:"+cs)
      cs
    }

  def main(args: Array[String]) {
    println("Java 1:"+calculate("Java"))
    println("Java 2:"+calculate("Java"))
    println("Scala :"+calculate("Scala"))
  }
}

  

ChecksumAccumulator单例对象有一个方法,calculate,用来计算所带的String参数中字符的校验和。它还有一个私有字段,cache,一个缓存之前计算过的校验和的可变映射。2方法的第一行,“if (cache.contains(s))”,检查缓存,看看是否传递进来的字串已经作为键存在于映射当中。如果是,就仅仅返回映射的值,“cache(s)”。否则,执行else子句,计算校验和。else子句的第一行定义了一个叫acc的val并用新建的ChecksumAccumulator实例初始化它。下一行是个for表达式,对传入字串的每个字符循环一次,并在其上调用toByte把字符转换成Byte,然后传递给acc所指的ChecksumAccumulator实例的add方法。完成了for表达式后,下一行的方法在acc上调用checksum,获得传入字串的校验和,并存入叫做cs的val。下一行,“cache += (s -> cs)”,传入的字串键映射到整数的校验和值,并把这个键-值对加入cache映射。方法的最后一个表达式,“cs”,保证了校验和为此方法的结果。

这里打印的结果是:

s:Java cs:-130
Java 1:-130
Java 2:-130
s:Scala cs:-228
Scala :-228

  

问题来了,ChecksumAccumulator单例对象是不能new的,但是在代码中出现了val acc = new ChecksumAccumulator,这不是矛盾吗?其实不然,这里new的其实是ChecksumAccumulator单例对象的伴生类,即ChecksumAccumulator类,而伴生类和伴生对象可以互相访问对方的私有成员,所以acc可以访问ChecksumAccumulator单例对象的cache变量。同理,那么第一次测试“Java”字符串的时候,acc实例执行了checksum()方法,接下来并把这个数据存到cache这个val中。在第二次测试“Java”字符串的时候,程序是直接从cache变量中获取到了数据,并返回。

这里也可以看出类和单例对象的一个差别是,单例对象是在第一次访问的时候初始化,不可以new,不能带参数,而类可以new,可以带参数。每个单例对象都被作为由一个静态变量指向的虚构类:synthetic class的一个实例来实现,因此它们与Java静态类有着相同的初始化语法。

参考:《Scala编程》

免责声明:文章转载自《Scala伴生类和伴生对象》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Lakehouse 架构解析与云上实践高仿微信新消息提示音功能下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Scala日期处理

计算时间间隔  val d = new java.text.SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new java.util.Date()) val dateFormat = new java.text.SimpleDateFormat("yyyyMMdd HH:mm:ss")...

在阿里云上搭建 Spark 实验平台

之前在自己的笔记本上运行 Python 代码,有些要运行一天多,一关机就前功尽弃,很不方便,所以才有租用阿里云服务器的想法,用了同学租的一台用了两天又觉得不够使,索性就自己租了三台,配置如下,三台一共约 320 块。 CPU:1核 内存:2048 MB 操作系统:Ubuntu 14.04 64位 带宽计费方式:按固定带宽 当前使用带宽:1Mbps 实例规格...

Scala学习——模式匹配

scala模式匹配 1.基础match case(类似java里switch case,但功能强大些) object MatchApp { def main(args: Array[String]): Unit = { val is = Array("a","b","c","d") val i = is(Random.nextInt...

Scala从入门到精通之四-映射和元组

在Scala中映射之键值对的集合,元组是n个对象的聚集,但是对象的类型不一定相同 本节内容要点   Scala中映射的创建,遍历和查询   如何从可变和不可变映射中做出选择   Scala映射和Java映射见的互操作   Scala中元组的使用 一.构造映射 我们可以这样来构造一个映射: val scores = Map("ysl"->100,"...

Flink 写数据到MySql (JDBC Sink)

POM 文件 <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-scala_2.11</artifactId> <ver...

Scala语言简介和开发环境配置

Scala语言的简介和开发环境搭建   Scala是一门结合了面向对象特征和函数式编程特征的语言,它是一个创新的编程语言产品。Scala可以做脚本(就像shell脚本一样),可以做服务端编程语言,可以写数据处理程序等,在很多方面都适用,但是目前主要应用的领域有:编写Web服务器端程序、编写基于Spark的数据处理程序。能做的事情很多,可是被采用的领域不多...