Scala学习笔记04【类和对象】

时间:2023-02-24 12:15:11

1、类,字段和方法

类的定义及创建对象:

class ChecksumAccumulator 
{
...//类定义里,可以放置字段和方法,这些被笼统地称为成员:member。
}

创建ChecksumAccumulator对象:

new CheckSumAccumulator 

又:

class ChecksumAccumulator 
{
var sum = 0
}

val acc = new ChecksumAccumulator //acc.sum = 0
val csa = new ChecksumAccumulator //csa.sum = 0

acc.sum = 3 //acc.sum = 3 , csa.sum = 0

对acc(或csa)不能做的事情是由于它们是val,而不是var,你不可以把它们再次赋值为不同的对象。

// 编译不过,因为acc是val 
acc = new ChecksumAccumulator

要声明字段是私有的,可以把访问修饰符private放在字段的前面,就像这样:

class ChecksumAccumulator 
{

private var sum = 0
}

有了这个ChecksumAccumulator的定义,任何从类外部访问sum的尝试都会失败:

val acc = new ChecksumAccumulator  
acc.sum = 5 //编译不过,因为sum是私有的

在Scala里把成员公开的方法是不显式地指定任何访问修饰符(即public可以省略)。

定义方法:

class ChecksumAccumulator 
{

private var sum = 0

def add(b: Byte): Unit = //参数b类型是val
{
sum += b
}

def checksum(): Int =
{
return ~(sum & 0xFF) + 1 //“return”是多余的
}
}

传递给方法的任何参数都可以在方法内部使用。
Scala里方法参数的一个重要特征是它们都是val,不是var(即参数b类型是val)。

return语句是多余的:
如果没有发现任何显式的返回语句,Scala方法将返回方法中最后一个计算得到的值


方法返回值问题(“=”的使用):

def f(): Unit = "abc"         // f: ()Unit 

def g() { "abc" } //g: ()Unit

def h() = { "abc" } // h: ()java.lang.String 返回String

2、分号推断

Scala程序里,语句末尾的分号通常是可选的。
如果一行里写多个语句那么分号是需要的:

val s = "hello"; println(s) 

代码

x  
+ y

会被分成两个语句x和+ y。

希望把它作为一个语句x + y,你可以把它包裹在括号里:

(x  
+ y)

x +
y

在串接类似于+的中缀操作符,把操作符放在行尾而不是行头是普遍的Scala风格:

x +  
y +
z //相当于x + y + z

分号推断的规则

除非以下情况的一种成立,否则行尾被认为是一个分号:

1.疑问行由一个不能合法作为语句结尾的字结束,如句点或中缀操作符。

2.下一行开始于不能作为语句开始的字。

3.行结束于括号(…)或方框[…]内部,因为这些符号不可能容纳多个语句。


3、Singleton对象

Scala比Java更面向对象的一个方面是Scala没有静态成员。
Scala有单例对象:singleton object。

除了用object关键字替换了class关键字以外,单例对象的定义看上去就像是类定义:

//单例对象
import scala.collection.mutable.Map

object ChecksumAccumulator
{
private val cache = Map[String, Int]()

def calculate(s: String): Int = //计算所带的String参数中字符的校验和
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
}
}

当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。
必须在同一个源文件里定义类和它的伴生对象。
类被称为是这个单例对象的伴生类:companion class。
类和它的伴生对象可以互相访问其私有成员。

调用ChecksumAccumulator单例对象的calculate方法:

ChecksumAccumulator.calculate("Every value is an object.") 

类和单例对象间的一个差别是,单例对象不带参数,而类可以。

因为不能用new关键字实例化一个单例对象,没机会传递给它参数。

每个单例对象都被作为由一个静态变量指向的虚构类:synthetic class的一个实例来实现。
因此它们与Java静态类有着相同的初始化语法,单例对象会在第一次被访问的时候初始化。


4、Scala程序

执行Scala程序,要提供一个有main方法的孤立单例对象名:

import ChecksumAccumulator.calculate  

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

注意:
Scala隐式引用了包java.lang和scala的成员,和名为Predef的单例对象的成员,到每个Scala源文件中。


5、Application特质

Scala提供了一个特质,scala.Application,可以节省一些手指的输入工作。

实例:

import ChecksumAccumulator.calculate  

object FallWinterSpringSummer extends Application
{
for (season <- List("fall", "winter", "spring"))
println(season +": "+ calculate(season))
}

使用这个特质的方法是,首先在你的单例对象名后面写上”extends Application” 。
然后代之以main方法,你可以把想要放在main方法里的代码直接放在单例对象的大括号之间。

继承自Application比写个显式的main方法要短。


缺点:
首先,如果想访问命令行参数的话就不能用它,因为args数组不可访问。
第二,因为某些JVM线程模型里的局限,如果程序是多线程的就需要显式的main方法。