Scala 面向对象编程之类和对象


本文地址:http://www.6aiq.com/article/1534060694540
知乎专栏 点击关注
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出

定义一个类


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] 的使用,只有本对象内可以访问到。
f10818fb75114833a78821c41ba19c56.png

辅助 constructor

Scala 中,可以给类定义多个辅助 constructor,类似于 Java 中的构造函数重载,辅助 constructor 之间可以互相调用,而且必须第一行调用主 constructor。
bd0516b258b5419c8592d462d4ddd13e.png

主 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 中还可以通过使用默认参数,来给参数默认的值。
bcb758ffa4ef410289e5dfdfc79105fd.png
如果主 constructor 传入的参数什么修饰都没有,比如 name:String,那么如果类内部的方法使用到了,则会声明为 private[this] name,否则没有该 field,就只能被 constructor 代码使用而已。

8661bfc9236f4ecc94de3fa631e84a11.png



内部类

Scala 中,同样可以在类中定义内部类,但是与 Java 不同的是,每个外部类的对象的内部类,都是不同的类。

c2.Student 类,c1.Student 类,是不同的外部类的实例的不同的类。

cec582b9a5c84806ae5d3ed69bd63b1c.png

object

object,相当于 class 的单个实例,通常在里面放一些静态的 field 或者 method,第一次调用 object 的方法时,就会执行 object 的 constructor,也就是 object 内部不在 method 中的代码,但是 object 不能定义接受参数的 constructor。

object 的 constructor 只会在其第一次被调用时执行一次,以后再次调用就不会再次执行 constructor 了。

object 通常用于作为单例模式的实现,或者放 class 的静态成员,比如工具方法。
de5f6473173a4194a39e4a2813eb1ea6.png

伴生对象

如果有一个 class,还有一个与 class 同名的 object,那么就称这个 object 是 class 的伴生对象,class 是 object 的伴生类。伴生类和伴生对象必须存放在一个.scala 文件之中,伴生类和伴生对象,最大的特点在于,互相可以访问 private field。
062c3a738fab44c5977e564c37546582.png

object 继承抽象类

object 的功能和 class 类似,除了不能定义接收参数的 constructor 之外,object 也可以继承抽象类,并覆盖抽象类中的方法。
1e79135d72f44a269fe9caa154cf520b.png

apply 方法

object 中重要的一个特殊方法,apply 方法,通常在伴生对象中实现 apply 方法,并在其中实现构造伴生类的对象的功能。而创建伴生类的对象时,通常不会使用 new Class 的方式,而是使用 Class() 的方式,隐式调用伴生对象的 apply 方法,让对象创建更简洁。如 Array 类的伴生对象的 apply 方法就实现了接收可变数量的参数,并创建一个 Array 对象的功能。
f1c87074db104d9c93118343bff7042f.png

用 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 来查找枚举值。
216a3d05c2664b1487bbec56ad3d02d3.png

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]


本文地址:http://www.6aiq.com/article/1534060694540
知乎专栏 点击关注
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出