1. 程式人生 > >scala筆記-面向物件程式設計(10)

scala筆記-面向物件程式設計(10)

定義一個簡單的類
// 定義類,包含field以及方法
class HelloWorld {
  private var name = "leo"
  def sayHello() { print("Hello, " + name) }  
  def getName = name
}

// 建立類的物件,並呼叫其方法
val helloWorld = new HelloWorld
helloWorld.sayHello() 
print(helloWorld.getName) // 也可以不加括號,如果定義方法時不帶括號,則呼叫方法時也不能帶括號
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]
class Student {
  var name = "leo"
}

// 呼叫getter和setter方法,分別叫做name和name_ =
val leo = new Student
print(leo.name)
leo.name = "leo1"
自定義getter與setter
// 如果只是希望擁有簡單的getter和setter方法,那麼就按照scala提供的語法規則,根據需求為field選擇合適的修飾符就好:var、val、private、private[this]
// 但是如果希望能夠自己對getter與setter進行控制,則可以自定義getter與setter方法
// 自定義setter方法的時候一定要注意scala的語法限制,簽名、=、引數間不能有空格
class Student {
  private var myName = "leo"
  def name = "your name is " + myName
  def name_=(newValue: String)  {
    print("you cannot edit your name!!!")
  }
}

val leo = new Student
print(leo.name)
leo.name = "leo1"
僅暴露field的getter方法
// 如果你不希望field有setter方法,則可以定義為val,但是此時就再也不能更改field的值了
// 但是如果希望能夠僅僅暴露出一個getter方法,並且還能通過某些方法更改field的值,那麼需要綜合使用private以及自定義getter方法
// 此時,由於field是private的,所以setter和getter都是private,對外界沒有暴露;自己可以實現修改field值的方法;自己可以覆蓋getter方法
class Student {
  private var myName = "leo"

  def updateName(newName: String) { 
    if(newName == "leo1") myName = newName 
    else print("not accept this new name!!!")
  }

  def name = "your name is " + myName
}
private[this]的使用
// 如果將field使用private來修飾,那麼代表這個field是類私有的,在類的方法中,可以直接訪問類的其他物件的private field
// 這種情況下,如果不希望field被其他物件訪問到,那麼可以使用private[this],意味著物件私有的field,只有本物件內可以訪問到
class Student {
  private var myAge = 0
  def age_=(newValue: Int) { 
    if (newValue > 0) myAge = newValue 
    else print("illegal age!") 
  }
  def age = myAge
  def older(s: Student) = {
    myAge > s.myAge
  }
}
Java風格的getter和setter方法
// Scala的getter和setter方法的命名與java是不同的,是field和field_=的方式
// 如果要讓scala自動生成java風格的getter和setter方法,只要給field新增@BeanProperty註解即可
// 此時會生成4個方法,name: String、name_=(newValue: String): Unit、getName(): String、setName(newValue: String): Unit
import scala.reflect.BeanProperty
class Student {
  @BeanProperty var name: String = _
}
class Student(@BeanProperty var name: String)

val s = new Student
s.setName("leo")
s.getName()
輔助constructor
// Scala中,可以給類定義多個輔助constructor,類似於java中的建構函式過載
// 輔助constructor之間可以互相呼叫,而且必須第一行呼叫主constructor
class Student {
  private var name = ""
  private var age = 0
  def this(name: String) {
    this()
    this.name = name
  }
  def this(name: String, age: Int) {
    this(name)
    this.age = age
  }
}
主constructor
// Scala中,主constructor是與類名放在一起的,與java不同
// 而且類中,沒有定義在任何方法或者是程式碼塊之中的程式碼,就是主constructor的程式碼,這點感覺沒有java那麼清晰
class Student(val name: String, val age: Int) {
  println("your name is " + name + ", your age is " + age)
}

// 主constructor中還可以通過使用預設引數,來給引數預設的值
class Student(val name: String = "leo", val age: Int = 30) {
  println("your name is " + name + ", your age is " + age)
}

// 如果主constrcutor傳入的引數什麼修飾都沒有,比如name: String,那麼如果類內部的方法使用到了,則會宣告為private[this] name;否則沒有該field,就只能被constructor程式碼使用而已
內部類
// Scala中,同樣可以在類中定義內部類;但是與java不同的是,每個外部類的物件的內部類,都是不同的類
import scala.collection.mutable.ArrayBuffer
class Class {
  class Student(val name: String) {}
  val students = new ArrayBuffer[Student]
  def getStudent(name: String) =  {
    new Student(name)
  }
}

val c1 = new Class
val s1 = c1.getStudent("leo")
c1.students += s1

val c2 = new Class
val s2 = c2.getStudent("leo")
c1.students += s2
object
// object,相當於class的單個例項,通常在裡面放一些靜態的field或者method
// 第一次呼叫object的方法時,就會執行object的constructor,也就是object內部不在method中的程式碼;但是object不能定義接受引數的constructor
// 注意,object的constructor只會在其第一次被呼叫時執行一次,以後再次呼叫就不會再次執行constructor了
// object通常用於作為單例模式的實現,或者放class的靜態成員,比如工具方法

object Person {
  private var eyeNum = 2
  println("this Person object!")
  def getEyeNum = eyeNum
}
伴生物件
// 如果有一個class,還有一個與class同名的object,那麼就稱這個object是class的伴生物件,class是object的伴生類
// 伴生類和伴生物件必須存放在一個.scala檔案之中
// 伴生類和伴生物件,最大的特點就在於,互相可以訪問private field

object Person {
  private val eyeNum = 2
  def getEyeNum = eyeNum
}

class Person(val name: String, val age: Int) {
  def sayHello = println("Hi, " + name + ", I guess you are " + age + " years old!" + ", and usually you must have " + Person.eyeNum + " eyes.")
}
讓object繼承抽象類
// object的功能其實和class類似,除了不能定義接受引數的constructor之外
// object也可以繼承抽象類,並覆蓋抽象類中的方法

abstract class Hello(var message: String) {
  def sayHello(name: String): Unit
}

object HelloImpl extends Hello("hello") {
  override def sayHello(name: String) = {
    println(message + ", " + name)
  }
}
apply方法
// object中非常重要的一個特殊方法,就是apply方法
// 通常在伴生物件中實現apply方法,並在其中實現構造伴生類的物件的功能
// 而建立伴生類的物件時,通常不會使用new Class的方式,而是使用Class()的方式,隱式地呼叫伴生物件得apply方法,這樣會讓物件建立更加簡潔

// 比如,Array類的伴生物件的apply方法就實現了接收可變數量的引數,並建立一個Array物件的功能
val a = Array(1, 2, 3, 4, 5)

// 比如,定義自己的伴生類和伴生物件
class Person(val name: String)
object Person {
  def apply(name: String) = new Person(name)
}
main方法
// 就如同java中,如果要執行一個程式,必須編寫一個包含main方法類一樣;在scala中,如果要執行一個應用程式,那麼必須有一個main方法,作為入口
// scala中的main方法定義為def main(args: Array[String]),而且必須定義在object中
object HelloWorld {
  def main(args: Array[String]) {
    println("Hello World!!!")
  }
}

// 除了自己實現main方法之外,還可以繼承App Trait,然後將需要在main方法中執行的程式碼,直接作為object的constructor程式碼;而且用args可以接受傳入的引數
object HelloWorld extends App {
  if (args.length > 0) println("hello, " + args(0))
  else println("Hello World!!!")
}


// 如果要執行上述程式碼,需要將其放入.scala檔案,然後先使用scalac編譯,再用scala執行

scalac HelloWorld.scala
scala -Dscala.time HelloWorld

// App Trait的工作原理為:App Trait繼承自DelayedInit Trait,scalac命令進行編譯時,會把繼承App Trait的object的constructor程式碼都放到DelayedInit Trait的delayedInit方法中執行
用object來實現列舉功能
// Scala沒有直接提供類似於Java中的Enum這樣的列舉特性,如果要實現列舉,則需要用object繼承Enumeration類,並且呼叫Value方法來初始化列舉值
object Season extends Enumeration {
  val SPRING, SUMMER, AUTUMN, WINTER = Value
}

// 還可以通過Value傳入列舉值的id和name,通過id和toString可以獲取; 還可以通過id和name來查詢列舉值
object Season extends Enumeration {
  val SPRING = Value(0, "spring")
  val SUMMER = Value(1, "summer")
  val AUTUMN = Value(2, "autumn")
  val WINTER = Value(3, "winter")
}
Season(0)
Season.withName("spring")

// 使用列舉object.values可以遍歷列舉值
for (ele <- Season.values) println(ele)