Fork me on GitHub

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


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