类是对象的蓝图。一旦你定义了类,就能够用关键字new根据类的蓝图建立对象。在类的定义里,能够放置字段和方法,这些被笼统地称为成员。对于字段,不论是val仍是var定义的,都是指向对象的变量。对于方法,用def定义,包含了可执行代码。字段保留了对象的状态或数据,而方法使用这些数据执行对象的运算工做。当类被实例化的时候,运行时环境会预留一些内存来保留对象的状态映像——即变量的内容。html
建立类示例:java
class SumAccumulator { var sum = 0 }
而后实例化两次:程序员
val acc: SumAccumulator = new SumAccumulator val csa: SumAccumulator = new SumAccumulator
这时内存里对象的状态映像以下:缓存
因为字段sum是var类型而非val,因此sum能够被赋予新的Int类型的值:例如安全
acc.sum = 3
此时映像会变成:网络
图中同时存在两个sum变量,一个在acc指向的对象里,另外一个在csa指向的对象里。字段的另外一种说法是实例变量(instance variable),由于每个实例都有本身的变量集。多线程
尽管acc和csa都是val类型,可是仍是能够修改acc指向的对象的值(上面的sum)。val类型对象对acc(或csa)的限制仅限于不能够把它们再次赋值给其余对象。例如,下面的尝试将会失败:app
//编译不过,由于acc是val acc = new ChecksumAccumuulator
所以,咱们能够得出结论,acc将始终指向初始化的Checksuumaccumulator对象,可是对象包含的字段能够随时改动。ide
为了保证对象的健壮性,能够把类中字段变为私有的(private)以阻止外界直接对它访问。由于私有字段只能被定义成在同一类里的方法访问,全部跟新字段的代码将锁定在类里。要声明字段是私有的,能够把访问修饰符private放在字段的前面。函数
代码示例:
class SumAccumulator { private var sum = 0 }
使用private修饰后,任何从类外部对sum的访问都将失败
val acc: SumAccumulator = new SumAccumulator acc.sum = 3 //编译不经过,由于sum是私有的
1. Scala方法参数类型
Scala方法参数中的类型都是val而不是var类型
def add(a: Int, b: Int): Int = { a = a + b //编译错误:Reassignment to val (就是对val类型赋值的错误) a }
2. Scala方法中return
Scala方法中的return能够去掉,从而简化代码
代码示例:
def numSum(num:Int):Int ={ val sum = num + 10 sum //省略return关键字,简化代码 }
此时返回值就是sum
3. Scala方法中的“=”号
Scala方法中的“=”号很是重要:由于Scala编译器能够把任何类型转换为Unit,若是定义的函数中”=”号忘记了添加,那么Scala会默认它返回值为Unit类型。若你原本想返回一个String类型的值,因为没有添加”=”,String类型返回值会被转换为Unit类型而丢弃掉。
代码示例:
def printStr() { "这是String类型的返回值" }
返回结果为: ()
正确代码示例:
def printStr(): String = { "这是String类型的返回值" }
返回结果为: 这是String类型的返回值
4. Scala方法表达式
假如某个方法仅计算单个结果的表达式,这能够去掉花括号,若是结果表达式很短,甚至能够把它放在def的同一行里。
代码示例:
def numSum(num:Int):Int = num + 10
5. Scala中分号推断
scala程序中,语句末尾的分号一般是可选的,若一行仅有一个语句也能够不加分号。不过,若是一行包含多条语句时,分号则是必须的。
不加分号:
if(x < 2) println("too small") else println("ok")
必须加分号:
val x = 10; println(x) //两个语句,必须加分号
Scala一般的风格是把操做符放在行尾而不是行头:
错误示例:
val x = 10; val y = 3 val z = x //它会被编译器识别为z = x ; +y 两个语句 +y
打印结果:10
正确示例:
val x = 10;
val y = 3
val z = x +
y
println(z)
打印结果:13
Scala分号推断规则:
6. Scala无参方法
调用Scala类中无参方法时,能够写上括号,也能够不写。对于类中的取值器来讲,去掉()是不错的风格。
代码示例:
object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.talk() //ok per.talk //一样ok } } class Person { def talk(): Unit = println("Talking") }
若是你想强制使用这种风格,能够在声明方法时不带()
代码示例:
per.talk() //此时这种写法是错误的 per.talk //OK的 class Person { def talk = println("Talking") }
Scala比Java更为面向对象的特色之一是Scala不能定义静态成员,而是代之以定义单例对象(singleton Object)。除了用object关键字替换了class关键字之外,单例对象的定义看上去与类定义一致。
例子:ChecksumAccumulator.scala源码:
class ChecksumAccumulator { private var sum = 0 def add(b:Byte){sum += b} def checksum():Int = ~(sum & 0xFF) + 1 } 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静态类有着相同的初始化语义。特别要指出的是,单例对象在第一次被访问的时候才会被初始化。
不与伴生类共享名称的单例对象被称为独立对象(standalone object)。它能够用在不少地方,例如做为相关功能方法的工具类,或者定义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)) } }
要执行Summer应用程序,须要把以上的代码写入文件Summer.scala中,由于Summer使用了ChecksumAccumulator,因此还要把ChecksumAccumulator的代码,上面的源码(类及它的伴生对象),放在文件ChecksumAccumulator.scala中。
Scala和Java之间有一点不一样,Java须要类名称与源码文件名同名,而在Scala对于源文件的命名没有硬性规定。然而一般状况下若是不是脚本,推荐的风格是像在Java里那样按照所包含的类名来命名文件,这样程序员就能够比较容易地根据文件名找到类。
Scala的脚步必须以结果表达式介绍。所以若是你尝试以脚本方式执行Summer.scala,Scala解释器将会报错说Summer.scala不是以结果表达式结束的。正确作法是:须要用Scala编译器真正的编译这些文件,而后执行输出的类文件,方式之一使用Scala的基本编译器,scalac。
D:\work\workspace\scala>scalac ChecksumAccumulator.scala Summer.scala
D:\work\workspace\scala>
D:\work\workspace\scala>fsc ChecksumAccumulator.scala Summer.scala
D:\work\workspace\scala>
D:\work\workspace\scala>scala Summer of love of: -213 love: -182 D:\work\workspace\scala>
Scala类中使用公有字段的话,任何人均可以修改这个字段,使得安全性大大下降。因此咱们更倾向于使用getter和setter方法:
Scala对类中每一个字段都提供了getter和setter方法,分别叫作age和age_=,
代码示例:
建立一个exam1.scala文件,文件内容以下
class Person { var age = 0 }
若是想查看这些方法,能够先编译Person类,用scalac命令编译,而后用javap查看字节码:
D:\work\workspace\scala>scalac exam1.scala D:\work\workspace\scala>javap Person Compiled from "exam1.scala" public class Person { public int age(); public void age_$eq(int); public Person(); } D:\work\workspace\scala>
你能够本身从新定义getter和setter方法。
代码示例:
object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.age = 18 per.age = 16 //因为在setter里面控制了age不能变小,因此执行结果age不会变 println(per.age) per.age = 19 //使用setter,赋予大于原来的age println(per.age) } } class Person { private var privateAge = 0 //变成私有变量并更名 def age = privateAge def age_=(newAge: Int) { // age_= 不能有空格 if (newAge > privateAge) privateAge = newAge //使得年龄不能变小 } }
打印结果为:
Scala中每一个字段生成getter和setter的限制:
Scala在实现类中属性时的四个选择:
JavaBean规范把Java属性定义为一堆getFoo和setFoo方法。相似于Java,当你将Scala字段标注为 @BeanProperty时,getter和setter方法会自动生成。
代码示例:
import scala.beans.BeanProperty object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.name = "zhagnsan" per.setName("lisi") //BeanProperty生成的setName方法 println(per.getName) //BeanProperty生成的getName方法 } } class Person { @BeanProperty var name:String = _ }
打印结果为:
Lisi
上述类Person中由@BeanProperty生成了四个方法:
name: String name_= (newValue: String): Unit getName(): String setName (newValue: String): Unit
图示为针对字段生成的方法:
和java或C++同样,Scala也能够有任意多的构造器。不过,Scala类有一个构造器比其余全部构造器都更为重要,它就是主构造器。除了主构造器外,Scala类还能够有任意多的辅助构造器。
Scala中辅助构造器和Java或C++十分类似,只有两处不一样:
这里有一个带有两个辅助构造器的类。
代码示例:
object exam1 { def main(args: Array[String]): Unit = { val per1: Person = new Person //主构造器 val per2: Person = new Person("Bob") //第一个辅助构造器 val per3: Person = new Person("Bob",18) //第二个辅助构造器 } } class Person { private var name = "" private var age = 0 //一个辅助构造器 def this(name: String) { this() //调用主构造器 this.name = name } //另外一个辅助构造器 def this(name: String, age: Int) { this(name) //调用前一个辅助构造器 this.age = age } }
在Scala中,每一个类都有主构造器。主构造器并不以this方法定义,而是与类定义交织在一块儿。
主构造器的参数直接放在类名以后
代码示例:
object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person("Bob", 18) //使用主构造器实例化对象 println(per.name + " : " + per.age) } } class Person(val name: String, val age: Int) { //产生私有的name和age,没有setter //..... }
打印结果:
Bob : 18
上述简短的Person类定义极大简化了相同功能的Java代码:
与上例相同功能的Java代码示例:
class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String name() { return this.name; } public int age() { return this.age; } }
Scala主构造器定义的全部语句都会执行
代码示例:
class Person(val name: String, val age: Int) { //产生私有的name和age,没有setter println("主构造器定义的全部语句都会执行") //每次使用主构造器时都会执行 def description = name + "is" + age + "years old" }
不一样类型的主构造器参数对应会生成的字段和方法:
若是想让主构造器变成私有的,能够像这样放置private关键字:
class Person private (val name: String, val age: Int) { //...... }
这样一来,类用户就必须经过辅助构造器来构造Person对象了.
在Scala中,你几乎能够在任何语法结构中内嵌任何语法构造。你能够在函数中定义函数,在类中定义类。
代码示例:
class Network { //在Network类中定义类Member class Member(val name: String) { val contacts = new ArrayBuffer[Member] } private val members = new ArrayBuffer[Member] def join(name: String) = { val m = new Member(name) members += m m } }
考虑有以下两个网络:
al chatter = new Network val myFace = new Network
在Scala中,每一个实例都有它本身的Member类(内部类),就和它们有本身的members(内部类)字段同样。也就是说chatter.Member和myFace.Member是不一样的两个类。
做用:拿网络示例来讲,你能够在各自的网络中添加成员,可是不能跨网添加成员。
代码示例:
val chatter = new Network val myFace = new Network val fred = chatter.join("Fred") val wilma = chatter.join("wilma") fred.contacts += wilma //OK val barney = myFace.join("Barney") //错误:不能将一个myFace.Member添加到chatter.Member元素缓冲中 fred.contacts += barney
在嵌套类中,你能够经过 外部类.this 的方式来访问外部类的this引用,就像Java那样。
class Network(val name: String) { //在Network类中定义类Member class Member(val name: String) { //.... def description = name + "inside" + Network.this.name } }
若是你以为须要,也可使用以下语法创建一个指向该引用的别名:
class Network(val name: String) { outer => //在Network类中定义类Member class Member(val name: String) { //.... def description = name + "inside" + outer.name } }
上述语法使得outer变量指向Network.this。对这个变量,你可使用任何合法的名称。
转自:https://blog.csdn.net/u011204847/article/details/51105362