类 / 对象
【《快学Scala》笔记】
一、类
1、Scala中的类是公有可见性的,且多个类可以包含在同一个源文件中;
1 class Counter{ 2 private var value = 0 //类成员变量必须初始化,否则报错 3 4 def increment(){ //类中的方法默认是公有可见性 5 value += 1 6 } 7 8 def current() = value //对于类中的“取值方法”,在定义时可省略掉括号,直接 def current = value 9 }
Scala类的使用:
Scala的类在未提供构造器时,也会提供默认构造器;且在调用无参构造器或无参方法时可省略掉方法后的括号。
2、Scala类的每个字段都有getter和setter方法,私有字段的getter和setter默认是私有的,公有字段的getter和setter方法默认是公有的。
其中对于类中的属性value,Scala类默认生成的getter方法名为 value,默认生成的setter方法名为 value_= 。使用时,我们可以重新定义获取或设置属性的方法。
例如,
1 class Clock{ 2 var hour = 0 3 var minute = 0 4 var second = 0 5 6 def getTime():String={ 7 return hour+":"+minute+":"+second 8 } 9 }
对于Clock类中的属性,如 hour,其对应的getter方法为 hour ,其对应的setter方法名为 hour_=
【注:可以重新定义获取Scala类中属性的方法,但是最好不要与属性默认对应的getter/setter方法重名,否则会报错。】
3、对于Scala类中的val属性,只有默认的getter方法;对于private属性,其默认getter、setter都是private的。因而,对于不想提供setter方法的变量可以设置为val,对于不想提供getter、setter方法的变量可以设置为private。
4、注意,Scala类中,定义无参函数时,若函数声明时省略了函数名后的括号(由于无参数,可以省略),调用时,必须参数无括号的形式,通过带括号形式调用会报错。如,
1 class Counter{ 2 var value = 0 3 4 def current = value 5 }
类Counter定义中,对于方法current,由于不接受参数,所以定义时省略了方法名current后的括号。此时,对于Counter的实例counter,调用current方法时,必须采用counter.current(无括号形式)。
5、对于Scala类中定义的字段,在Scala中实际为私有字段。(还有待进一步理解和验证)
1 class Counter{ 2 var value = 0 3 val sum = 0 4 private var cnt = 1 5 6 //.... 7 }
在Counter类中,Scala在为Counter生成面向JVM的类时,对于value字段,会生成对应的私有字段value和公有的getter、setter方法;对于sum字段,会生成私有的final字段和公有的getter方法;对于cnt字段,会生成私有字段cnt和私有的getter、setter方法(貌似这个private声明只是影响的Scala根据属性生成的对应getter、setter的可见性)。
6、构造器
Scala的类可以有一个主构造器和多个辅助构造器。每个辅助构造器的名称为this,每一个辅助构造器都必须以调用已经定义的辅助构造器或主构造器开始定义。
- 主构造器
如果一个类没有显示定义主构造器,则有一个默认的无参主构造器。
如定义一个Student类,
1 class Student(val name:String, var age:Int = 0, address:String = "", private var school:String = ""){ 2 var grade:Int = if( age>7 ) age-7 else 0 3 4 println(" I‘m in main constructor. ") 5 6 def info() = " name is "+name+", age is "+age+", address is "+address 7 }
对于Scala类,主构造器的参数放置在类名后,由括号括起来。且对于主构造器中var、val、private 等标注的参数,都会成为类的对应字段,并生成对应的默认getter、setter方法。如Student类中的name、age、school等。对于主构造器中的未用var、val标注的参数,如果在类的任何一个方法用用到该参数,该参数将会转换为类的字段,否则不会,如Student类的address属性。
由于在Student类中的info方法中用到了参数address,所以Student共有name、age、address、school、grade等5个属性,且Scala根据对应属性的特点生成了默认的getter和setter方法。
对于主构造器的参数,也可以提供参数默认值。通过为主构造器提供默认值可减少辅助构造器的个数。
主构造器的函数体,是类中除了方法定义以外的其他语句,如在Student类的主构造器中,包含grade属性的初始化和prinln这两行语句。
Stuent类的使用。
- 辅助构造器
辅助构造器通过this来定义,且必须首先调用主构造器或者其他已经定义的辅助构造器。
1 class Person(val name:String){ 2 var age = 0 3 var sex:Char = ‘f‘ 4 5 println("main constructor...") 6 7 def this(name:String, age:Int){ 8 this(name) //调用主构造器 9 this.age = age //使用this关键字 10 11 println(" auxiliary constructor1 ") 12 } 13 14 def this(name:String, age:Int, sex:Char){ 15 this(name, age) 16 17 this.sex = sex 18 19 println(" auxiliary constructor2 ") 20 } 21 }
【注:辅助构造器的参数前不能添加val、var标志,否则会报错。】
- 私有主构造器
1 class Person private(val name:String){ 2 var age:Int = 1 3 4 def this(name: String, age:Int){ 5 this(name) 6 this.age = age 7 } 8 9 }
私有构造器通过在类名后用private关键字标注主构造器参数来标明。此时,可以通过辅助构造器来创建该类的对象。
7、嵌套类
1 class Family(val h_name:String, val w_name:String){ 2 class Husband(var name:String){ 3 println(" I‘m a husband ") 4 } 5 6 class Wife(var name:String){ 7 println(" I‘m a Wife ") 8 } 9 10 var husband = new Husband(h_name) 11 var wife = new Wife(w_name) 12 13 def info(){ 14 println( "husband: "+husband.name+", wife:"+wife.name ) 15 } 16 }
在Scala中,你几乎可以在任何语法结构中嵌套任何语法结构,如在函数中定义函数,在类中定义类。
二、对象(object)
1、Scala中没有静态方法和静态字段,但是可以用object语法来实现类似的功能。对象定义了某个类的单个实例。
Scala的object中可以用来实现类似的功能,用来存放工具函数或常量等。如,
1 object Sequence{ 2 private var next_num = 0 3 val threshold = 100 4 5 def getSequence() = { 6 next_num += 1 7 next_num 8 } 9 }
使用object中的常量或方法,通过object名直接调用,对象构造器在对象第一次被使用时调用(如果某对象一直未被使用,那么其构造器也不会被调用)。
object的构造器不接受参数传递。
2、伴生对象
可以将在Java类中定义的静态常量、方法等放置到Scala的类的伴生对象中。伴生对象与类同名,且必须放置在同一源文件中。类可以访问伴生对象私有特性,但是必须通过 伴生对象.属性名 或 伴生对象.方法 调用。
伴生对象是类的一个特殊实例。
1 class Counter{ 2 def getTotalCounter()= Counter.getCount 3 } 4 5 object Counter{ 6 private var cnt = 0 7 8 private def getCount()= cnt 9 }
如在类Counter访问其伴生对象的石油方法getCount,必须通过 Counter.getCount() 的方式调用。
3、对象可以继承或扩展多个特质
1 abstract class Person(var name:String, var age:Int){ 2 def info():Unit 3 } 4 5 object XiaoMing extends Person("XiaoMing", 5){ 6 def info(){ 7 println(" name is "+name+", age is "+age) 8 } 9 }
4、apply方法
当遇到 object(参数1, 参数2,....,参数n)的形式的调用时,apply方法便会被调用。
5、main方法——Scala程序的入口
main方法定义在object中,形式如下:
1 object HelloWorld{ 2 def main(args: Array[String]){ 3 println("Hello World!") 4 } 5 }
可以通过scalac 源文件名,然后通过 scala 类名 来执行主程序。
6、还可以通过扩展特质App来运行指定代码
1 object HelloWorld2 extends App{ 2 println(" A ‘Hello world‘ from HelloWorld2 ") 3 }
通过扩展App特质的方式执行程序时,将要执行的程序放到了object的主构造器中。
7、枚举
Scala并没有定义枚举类型,但是可以通过定义扩展Enumeration的对象,并用Value方法初始化枚举类中的所有可选值,提供枚举。
1 object TrafficeLight extends Enumeration{ 2 val Red, Yellow, Green = Value 3 4 }
上述实例中的val Red, Yellow, Green = Value语句,相当于
1 val Red = Value 2 val Yellow = Value 3 val Green = Value
用Value方法初始化枚举类变量时,Value方法会返回内部类的新实例,且该内部类也叫Value。另外,在调用Value方法时,也可传入ID、名称两参数。如果未指定ID,默认从零开始,后面参数的ID是前一参数ID值加1。如果未指定名称,默认与属性字段同名。
1 object TrafficLight extends Enumeration{ 2 val Red = Value(1, "Stop") 3 val Yellow = Value("Wait") //可以单独传名称 4 val Green = Value(4) //可以单独传ID 5 }
上例中,Yellow属性就仅定义了名称,Green仅定义ID。
参数在不指定名称时,默认参数的Value为字段名。
【注:枚举类型的值是, 对象名.Value ,如上例中的枚举类型是 TrafficLight.Value。】
8、通过id方法获取枚举类型值的ID
9、通过values方法获取所有枚举值的集合
10、通过ID来获取对应的枚举对象