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]