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方法。