Scala 面向对象编程之类和对象
定义一个类
1 // 定义类,包含field及方法
2 scala> :paste
3 // Entering paste mode (ctrl-D to finish)
4 class HelloWorld {
5 private var name = "leo"
6 def sayHello() {print("Hello, " + name)}
7 def getName = name
8 }
9 // Exiting paste mode, now interpreting.
10 defined class HelloWorld
11 // 创建类的对象,并调用其方法
12 scala> val helloWorld = new HelloWorld
13 helloWorld: HelloWorld = HelloWorld@380e4452
14 // 如果方法无参,可以不加括号,如果定义方法时不带括号,则调用方法时也不能带括号
15 scala> helloWorld.sayHello()
16 Hello, leo
17 scala> helloWorld.sayHello
18 Hello, leo
19 scala> helloWorld.getName
20 res9: String = leo
21 scala> helloWorld.getName()
22 :14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
23 Unspecified value parameter index.
24 helloWorld.getName()
25 ^
getter与setter
定义不带private的var field,此时scala生成的面向JVM的类时,会定义为private的name字段,并提供public的getter和setter方法。
而如果使用private修饰field,则生成的getter和setter也是private的。
如果定义val field,则只会生成getter方法。
如果不希望生成setter和getter方法,则将field声明为private[this]。
1 scala> class Student {
2 | var name = "leo"
3 | }
4 defined class Student
5 scala> val s = new Student
6 s: Student = Student@2a88981e
7 // 调用getter和setter方法,分别叫做name和name_=
8 scala> s.name
9 res0: String = leo
10 scala> s.name()
11 <console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
12 Unspecified value parameter index.
13 s.name()
14 ^
15 scala> s.name = "jack"
16 s.name: String = jack
17 scala> s.name
18 res2: String = jack
19 scala> s.name_ = "jen"
20 <console>:15: error: value name_ is not a member of Student
21 val $ires1 = s.name_
22 ^
23 <console>:13: error: value name_ is not a member of Student
24 s.name_ = "jen"
25 ^
自定义getter与setter
如果只是希望拥有简单的getter和setter方法,那么就按照Scala提供的语法规则,根据需求为field选择合适的修饰符就好:var、val、private、private[this]。
如果希望能够自己对getter与setter进行控制,则可以自定义getter与setter方法。
自定义setter方法的时候一定要注意Scala的语法限制,签名、=、参数间不能有空格。
1 scala> :paste
2 // Entering paste mode (ctrl-D to finish)
3class Student {
4 private var myName = "leo"
5 def name = "your name is " + myName
6 def name_ = (newName:String) {
7 print("you cannot edit your name!")
8 }
9}
10 // Exiting paste mode, now interpreting.
11 <console>:14: error: not found: value newName
12 def name_ = (newName:String) {
13 ^
14 scala> :paste
15 // Entering paste mode (ctrl-D to finish)
16 class Student {
17 private var myName = "leo"
18 def name = "your name is " + myName
19 def name_=(newName:String) {
20 print("you cannot edit your name!")
21 }
22 }
23 // Exiting paste mode, now interpreting.
24 defined class Student
25 scala> val s = new Student
26 s: Student = Student@4fc8cd5c
27 scala> val s = new Student()
28 s: Student = Student@1dafc862
29 scala> s.name
30 res3: String = your name is leo
31scala> s.name = "leo1"
32you cannot edit your name!s.name: String = your name is leo
private[this]的使用
如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以访问类的其他对象的private field。这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对象私有的field,只有本对象内可以访问到。
1 scala> :paste
2// Entering paste mode (ctrl-D to finish)
3 class Student {
4 private var myAge = 0
5 def age_=(newAge:Int) {
6 if(newAge>0) myAge = newAge
7 else println("illegal age!")
8 }
9 def age = myAge
10 def older(s:Student) = {
11 myAge > s.myAge
12 }
13 }
14// Exiting paste mode, now interpreting.
15 defined class Student
16 scala> val s1 = new Student
17 s1: Student = Student@7ba9d3ec
18 scala> s1.age = 20
19 s1.age: Int = 20
20 scala> val s2 = new Student
21 s2: Student = Student@2dd6713
22 scala> s2.age = 25
23 s2.age: Int = 25
24 scala> s1.older(s2)
25 res8: Boolean = false
private[this]的使用,只有本对象内可以访问到。
辅助constructor
Scala中,可以给类定义多个辅助constructor,类似于Java中的构造函数重载,辅助constructor之间可以互相调用,而且必须第一行调用主constructor。
主constructor
Scala中,主constructor是与类名放在一起的,与Java不同,而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有Java那么清晰。
1 scala> class Student(val name:String, val age:Int) {
2 | println("your name is " + name + ", your age is " + age)
3 | }
4 defined class Student
5 scala> val s = new Student
6 :14: error: not enough arguments for constructor Student: (name: String, age: Int)Student.
7 Unspecified value parameters name, age.
8 val s = new Student
9 ^
10 scala> val s = new Student("leo", 30)
11 your name is leo, your age is 30
12 s: Student = Student@60cdee08
主construntor中还可以通过使用默认参数,来给参数默认的值。
如果主constructor传入的参数什么修饰都没有,比如name:String,那么如果类内部的方法使用到了,则会声明为private[this] name,否则没有该field,就只能被constructor代码使用而已。
内部类
Scala中,同样可以在类中定义内部类,但是与Java不同的是,每个外部类的对象的内部类,都是不同的类。
c2.Student类,c1.Student类,是不同的外部类的实例的不同的类。
object
object,相当于class的单个实例,通常在里面放一些静态的field或者method,第一次调用object的方法时,就会执行object的constructor,也就是object内部不在method中的代码,但是object不能定义接受参数的constructor。
object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再次执行constructor了。
object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法。
伴生对象
如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类。伴生类和伴生对象必须存放在一个.scala文件之中,伴生类和伴生对象,最大的特点在于,互相可以访问private field。
object继承抽象类
object的功能和class类似,除了不能定义接收参数的constructor之外,object也可以继承抽象类,并覆盖抽象类中的方法。
apply方法
object中重要的一个特殊方法,apply方法,通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能。而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式调用伴生对象的apply方法,让对象创建更简洁。如Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能。
用object实现枚举功能
Scala没有直接提供类似于Java的Enum枚举特性,如果要实现枚举,则需要用object继承Enumeration,并且调用Value方法来初始化枚举值。
1 object Season extends Enumeration {
2 val SPRING, SUMMER, AUTUMN, WINTER = Value
3 }
4 scala> Season.SPRING
5 res1: Season.Value = SPRING
还可以通过Value传入枚举值的id和name,通过id和toString可以获取,还可以通过id和name来查找枚举值。
isInstanceOf和asInstanceOf
// 如果我们创建了子类的对象,但是又将其赋予了父类类型的变量。则在后续的程序中,我们又需要将父类类型的变量转换为子类类型的变量,应该如何做?
// 首先,需要使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转换为指定类型
// 注意,如果对象是null,则isInstanceOf一定返回false,asInstanceOf一定返回null
// 注意,如果没有用isInstanceOf先判断对象是否为指定类的实例,就直接用asInstanceOf转换,则可能会抛出异常
class Person
class Student extends Person
val p: Person = new Student
var s: Student = null
if (p.isInstanceOf[Student]) s = p.asInstanceOf[Student]
getClass和classOf
// isInstanceOf只能判断出对象是否是指定类以及其子类的对象,而不能精确判断出,对象就是指定类的对象
// 如果要求精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了
// 对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断
class Person
class Student extends Person
val p: Person = new Student
p.isInstanceOf[Person]
p.getClass == classOf[Person]
p.getClass == classOf[Student]