Scala学习笔记05_面向对象编程之类和对象

定义一个简单的类

 1// 定义类,包含field及方法
 2scala> :paste
 3// Entering paste mode (ctrl-D to finish)
 4class HelloWorld {
 5  private var name = "leo"
 6  def sayHello() {print("Hello, " + name)}
 7  def getName = name
 8}
 9// Exiting paste mode, now interpreting.
10defined class HelloWorld
11// 创建类的对象,并调用其方法
12scala> val helloWorld = new HelloWorld
13helloWorld: HelloWorld = HelloWorld@380e4452
14// 如果方法无参,可以不加括号,如果定义方法时不带括号,则调用方法时也不能带括号
15scala> helloWorld.sayHello()
16Hello, leo
17scala> helloWorld.sayHello
18Hello, leo
19scala> helloWorld.getName
20res9: String = leo
21scala> helloWorld.getName()
22<console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
23Unspecified 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]。

 1scala> class Student {
 2     | var name = "leo"
 3     | }
 4defined class Student
 5scala> val s = new Student
 6s: Student = Student@2a88981e
 7// 调用getter和setter方法,分别叫做name和name_=
 8scala> s.name
 9res0: String = leo
10scala> s.name()
11<console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
12Unspecified value parameter index.
13       s.name()
14             ^
15scala> s.name = "jack"
16s.name: String = jack
17scala> s.name
18res2: String = jack
19scala> s.name_ = "jen"
20<console>:15: error: value name_ is not a member of Student
21val $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的语法限制,签名、=、参数间不能有空格。

 1scala> :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                      ^
14scala> :paste
15// Entering paste mode (ctrl-D to finish)
16class 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.
24defined class Student
25scala> val s = new Student
26s: Student = Student@4fc8cd5c
27scala> val s = new Student()
28s: Student = Student@1dafc862
29scala> s.name
30res3: String = your name is leo
31scala> s.name = "leo1"
32you cannot edit your name!s.name: String = your name is leo

仅暴露field的getter方法

如果不希望field有setter方法,则可以定义为val,但是此时就再也不能更改field的值了。

如果希望能够仅仅暴露出一个getter方法,并且还能通过某些方法更改field的值,那么需要综合使用private以及自定义getter方法。此时,由于field是private的,所以setter和getter都是private,对外界没有暴露,自己可以实现修改field值的方法,自己可以覆盖getter方法。

 1scala> :paste
 2// Entering paste mode (ctrl-D to finish)
 3class Student {
 4  private var myName = "leo"
 5  def updateName(newName:String) {
 6    if(newName=="leo1") myName = newName
 7    else println("not accept this new name, " + newName)
 8  }
 9  def name = "your name is " + myName
10}
11// Exiting paste mode, now interpreting.
12defined class Student
13scala> val s = new Student
14s: Student = Student@3186acfb
15scala> s.name
16res4: String = your name is leo
17scala> s.updateName("leo2")
18not accept this new name, leo2
19scala> s.updateName("leo1")
20scala> s.name
21res7: String = your name is leo1

private[this]的使用

如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以访问类的其他对象的private field。这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对象私有的field,只有本对象内可以访问到。

 1scala> :paste
 2// Entering paste mode (ctrl-D to finish)
 3class 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.
15defined class Student
16scala> val s1 = new Student
17s1: Student = Student@7ba9d3ec
18scala> s1.age = 20
19s1.age: Int = 20
20scala> val s2 = new Student
21s2: Student = Student@2dd6713
22scala> s2.age = 25
23s2.age: Int = 25
24scala> s1.older(s2)
25res8: Boolean = false

private[this]的使用,只有本对象内可以访问到。

 1scala> :paste
 2// Entering paste mode (ctrl-D to finish)
 3class Student {
 4  private[this] 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<console>:23: error: value myAge is not a member of Student
16           myAge > s.myAge
17                     ^

Java风格的getter和setter方法

Scala的getter和setter方法的命名与Java是不同的,是fieldfield_=的方式。如果要让Scala自动生成Java风格的getter和setter方法,只要给field添加@BeanProperty注解即可。此时会生成4个方法,name:Stringname_=(newValue:String):UnitgetName():StringsetName(newValue:String):Unit

 1scala> import scala.reflect.BeanProperty
 2<console>:13: error: object BeanProperty is not a member of package reflect
 3       import scala.reflect.BeanProperty
 4              ^
 5scala> import scala.beans.BeanProperty
 6import scala.beans.BeanProperty
 7scala> :paste
 8// Entering paste mode (ctrl-D to finish)
 9class Student {
10  @BeanProperty var name:String = _
11}
12// Exiting paste mode, now interpreting.
13defined class Student
14scala> val s = new Student
15s: Student = Student@1828b826
16scala> s.setName("leo")
17scala> s.getName()
18res10: String = leo
19scala> s.name = "jack"
20s.name: String = jack
21scala> s.name
22res12: String = jack

在主构造函数方式加注解,

 1scala> class Student(@BeanProperty var name:String)
 2defined class Student
 3scala> val s = new Student("leo")
 4s: Student = Student@148bf213
 5scala> s.getName()
 6res17: String = leo
 7scala> s.setName("jack")
 8scala> s.getName
 9res19: String = jack
10scala> s.name
11res20: String = jack

辅助constructor

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

 1scala> :paste
 2// Entering paste mode (ctrl-D to finish)
 3class Student {
 4  private var name = ""
 5  private var age = 0
 6  def this(name:String) {
 7    this()
 8    this.name = name
 9  }
10  def this(name:String, age:Int) {
11    this(name)
12    this.age = age
13  }
14}
15// Exiting paste mode, now interpreting.
16defined class Student
17scala> val s1 = new Student
18s1: Student = Student@5eb6d96d
19scala> val s2 = new Student("leo")
20s2: Student = Student@b34c972
21scala> val s3 = new Student("leo", 30)
22s3: Student = Student@1182aa34

主constructor

Scala中,主constructor是与类名放在一起的,与Java不同,而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有Java那么清晰。

 1scala> class Student(val name:String, val age:Int) {
 2     |   println("your name is " + name + ", your age is " + age)
 3     | }
 4defined class Student
 5scala> val s = new Student
 6<console>:14: error: not enough arguments for constructor Student: (name: String, age: Int)Student.
 7Unspecified value parameters name, age.
 8       val s = new Student
 9               ^
10scala> val s = new Student("leo", 30)
11your name is leo, your age is 30
12s: Student = Student@60cdee08

主construntor中还可以通过使用默认参数,来给参数默认的值。

1scala> class Student(val name:String="leo", val age:Int=30) {
2     |   println("your name is " + name + ", your age is " + age)
3     | }
4defined class Student
5scala> val s = new Student
6your name is leo, your age is 30
7s: Student = Student@79eef059

如果主constructor传入的参数什么修饰都没有,比如name:String,那么如果类内部的方法使用到了,则会声明为private[this] name,否则没有该field,就只能被constructor代码使用而已。

 1scala> class Student(name:String="leo", age:Int=30) {
 2     |   println("your name is " + name + ", your age is " + age)
 3     | }
 4defined class Student
 5scala> val s = new Student("leo", 30)
 6your name is leo, your age is 30
 7s: Student = Student@115989a9
 8scala> s.name
 9<console>:14: error: value name is not a member of Student
10       s.name
11         ^

内部类

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

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

 1scala> :paste
 2// Entering paste mode (ctrl-D to finish)
 3import scala.collection.mutable.ArrayBuffer
 4class Class {
 5  class Student(val name:String)
 6  val students = new ArrayBuffer[Student]()
 7  def getStudent(name:String) = {
 8    new Student(name)
 9  }
10}
11// Exiting paste mode, now interpreting.
12import scala.collection.mutable.ArrayBuffer
13defined class Class
14scala> val c1 = new Class
15c1: Class = Class@7d6340d6
16scala> val s1
17     |  = c1.getStudent("leo")
18s1: c1.Student = Class$Student@2662e5cf
19scala> c1.students += s1
20res1: c1.students.type = ArrayBuffer(Class$Student@2662e5cf)
21scala> val c2 = new Class
22c2: Class = Class@d207f78
23scala> val s2 = c2.getStudent("leo")
24s2: c2.Student = Class$Student@56de11b8
25scala> c1.students += s2
26<console>:19: error: type mismatch;
27 found   : c2.Student
28 required: c1.Student
29       c1.students += s2
30                      ^

object

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

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

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

 1object Person {
 2  private var eyeNum = 2
 3  println("this is Person object constructor is execting")
 4  def getEyeNum = eyeNum
 5}
 6scala> Person.getEyeNum
 7this is Person object constructor is execting
 8res4: Int = 2
 9scala> Person.getEyeNum
10res5: Int = 2

伴生对象

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

 1class Person(val name:String, val age:Int) {
 2  def sayHello = println("Hi, " + name + "is" + age + "years old" + ", and you have " + Person.eyeNum + " eyes.")
 3}
 4object Person {
 5  private val eyeNum = 2
 6  def getEyeNum = eyeNum
 7}
 8scala> val p = new Person("leo", 30)
 9p: Person = Person@6b1e9a68
10scala> p.sayHello
11Hi, leois30years old, and you have 2 eyes.
12# 伴生类里面可以访问伴生对象的private field,在外面不行。
13scala> Person.eyeNum
14<console>:15: error: value eyeNum is not a member of object Person
15       Person.eyeNum
16              ^

object继承抽象类

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

 1abstract class Hello(var message:String) {
 2  def sayHello(name:String): Unit
 3}
 4object HelloImpl extends Hello("hello") {
 5  override def sayHello(name:String) = {
 6    println(message + "," + name)
 7  }
 8}
 9scala> HelloImpl.sayHello("leo")
10hello,leo

apply方法

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

 1scala> val a = Array(1,2,3)
 2a: Array[Int] = Array(1, 2, 3)
 3// 
 4class Person(val name:String)
 5object Person {
 6  def apply(name:String) = new Person(name)
 7}
 8scala> val p1 = new Person("leo")
 9p1: Person = Person@78f71925
10scala> val p2 = Person("leo")
11p2: Person = Person@d5b8b3f
12scala> p2.name
13res10: String = leo

main方法

在Scala中,main方法作为应用程序的入口,Scala中的main方法定义为def main(args:Array[String]),而且必须定义在object中。

1object HelloWorld {
2  def main(args:Array[String]) {
3    println("Hello World!")
4  }
5}
6scala> HelloWorld.main(_)
7res15: Array[String] => Unit = <function1>
8

除了自己实现main方法,还可以继承App Train,然后将需要在main方法中运行的代码,直接作为object的constructor代码,而且用args可以接受传入的参数。

1object HelloWorld extends App {
2    if (args.length > 0) println("hello, " + args(0))
3    else println("Hello, World!")
4}

AppTrait继承自DelayedInit Trait,scalac命令进行编译时,会把继承App Trait的object的consturctor代码都放到DelayedInit Trait的delayedInit方法中执行。

用object实现枚举功能

Scala没有直接提供类似于Java的Enum枚举特性,如果要实现枚举,则需要用object继承Enumeration,并且调用Value方法来初始化枚举值。

1object Season extends Enumeration {
2  val SPRING, SUMMER, AUTUMN, WINTER = Value
3}
4scala> Season.SPRING
5res1: Season.Value = SPRING

还可以通过Value传入枚举值的id和name,通过id和toString可以获取,还可以通过id和name来查找枚举值。

 1object Season extends Enumeration {
 2  val SPRING = Value(0, "spring")
 3  val SUMMER = Value(1, "summer")
 4  val AUTUMN = Value(2, "autumn")
 5  val WINTER = Value(3, "winter")
 6}
 7scala> Season.SPRING.id
 8res2: Int = 0
 9scala> Season.SPRING.toString
10res3: String = spring
11scala> Season(0)
12res4: Season.Value = spring
13scala> Season.withName("winter")
14res5: Season.Value = winter
15scala> for(ele <- Season.values) println(ele)
16spring
17summer
18autumn
19winter

本文首发于steem,感谢阅读,转载请注明。

https://steemit.com/@padluo


微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。

 


 

读者交流电报群

https://t.me/sspadluo


知识星球交流群

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值