scala 面向對象之 繼承


scala 面向對象之 繼承

scala


 

1.extends

Scala中,讓子類繼承父類,與Java一樣,也是使用extends關鍵字

繼承就代表,子類可以從父類繼承父類的field和method;然后子類可以在自己內部放入父類所沒有,子類特有的field和method;使用繼承可以有效復用代碼 
 
  1. class Person {
  2. private var name = "leo"
  3. def getName = name
  4. }
  5. class Student extends Person {
  6. private var score = "A"
  7. def getScore = score
  8. }

子類可以覆蓋父類的field和method;但是如果父類用final修飾,則該類是無法被繼承的,field和method用final修飾,field和method是無法被覆蓋的

 
  1. final class Person { //final embellish class Person it not be extends
  2. private var name = "leo"
  3. def getName = name
  4. }
  5. class Student extends Person { //error
  6. private var score = "A"
  7. def getScore = score
  8. }

eg:

scala> final class Person { | private var name = "leo" | def getName = name | } defined class Person scala> class Student extends Person { | private var score = "A" | def getScore = score | } <console>:8: error: illegal inheritance from final class Person class Student extends Person { 

override 父類final修飾的字段 方法

 
  1. class Person {
  2. private var name = "leo"
  3. final val age = 10; //final embellish field age it not be override by child class
  4. def getName = name
  5. }
  6. class Student extends Person {
  7. private var score = "A"
  8. def getScore = score
  9. override val age = 20 //error
  10. override def getName = "my name is" + super.getName
  11. }

eg:

scala> class Person { | private var name = "leo" | final val age = 10; //final embellish field age it not be override by child class | def getName = name | } defined class Person scala> class Student extends Person { | private var score = "A" | def getScore = score | override val age = 20 //error | override def getName = "my name is" + super.getName | } <console>:11: error: overriding value age in class Person of type Int(10); value age cannot override final member override val age = 20 //error 
 

2.isInstanceOf 和 asInstanceOf

如果我們創建了子類的對象,但是又將其賦予了父類類型的變量。則在后續的程序中,我們又需要將父類類型的變量轉換為子類類型的變量,應該如何做?

首先,需要使用isInstanceOf判斷對象是否是指定類的對象,如果是的話,則可以使用asInstanceOf將對象轉換為指定類型 注意,如果對象是null,則isInstanceOf一定返回false,asInstanceOf一定返回null 注意,如果沒有用isInstanceOf先判斷對象是否為指定類的實例,就直接用asInstanceOf轉換,則可能會拋出異常 
 
  1. class Person
  2. class Student extends Person
  3. val p: Person = new Student
  4. var s: Student = null //note:isInstanceOf and asInstanceOf use[] instead of ()
  5. if (p.isInstanceOf[Student]) s = p.asInstanceOf[Student]

但,isInstanceOf只能判斷是否為某個類或者某個類的子類,無法精確判斷。 
eg:

scala> print(p.isInstanceOf[Student]) true scala> print(p.isInstanceOf[Person]) true scala> 
 

3.getClass 和 classOf

isInstanceOf只能判斷出對象是否是指定類以及其子類的對象,而不能精確判斷出,對象就是指定類的對象 
如果要求精確地判斷對象就是指定類的對象,那么就只能使用getClass和classOf了 
對象.getClass可以精確獲取對象的類,classOf[類]可以精確獲取類,然后使用==操作符即可判斷

 
  1. class Person
  2. class Student extends Person
  3. val p: Person = new Student
  4. p.isInstanceOf[Person]
  5. p.getClass == classOf[Person]
  6. p.isInstanceOf[Student]
  7. p.getClass == classOf[Student]

eg:

scala> p.getClass == classOf[Person] res20: Boolean = false scala> p.getClass == classOf[Student] res21: Boolean = true 
 

4.使用匹配模式來進行類型判斷

但是在實際開發中,比如spark的源碼中,大量的地方都是使用了模式匹配的方式來進行類型的判斷,這種方式更加地簡潔明了,而且代碼得可維護性和可擴展性也非常的高 
使用模式匹配,功能性上來說,與isInstanceOf一樣,也是判斷主要是該類以及該類的子類的對象即可,不是精准判斷的

 
  1. class Person
  2. class Student extends Person
  3. val p: Person = new Student
  4. p match {
  5. case per: Person => println("it's Person's object")
  6. case _ => println("unknown type")
  7. }
 

3.protected

跟java一樣,scala中同樣可以使用protected關鍵字來修飾field和method,這樣在子類中就不需要super關鍵字,直接就可以訪問非private修飾的field和method(非private修飾的field可以直接訪問) 
還可以使用protected[this],則只能在當前子類對象中訪問父類的field和method,無法通過其他子類對象訪問父類的field和method

 
  1. class Person {
  2. protected var name: String = "leo"
  3. protected[this] var hobby: String = "game"
  4. }
  5. class Student extends Person {
  6. def sayHello = println("Hello, " + name)
  7. def makeFriends(s: Student) { //this s is not be current instance object
  8. println("my hobby is " + hobby + ", your hobby is " + s.hobby)// error
  9. }
  10. }

eg1: error

scala> class Person { | protected var name: String = "leo" | protected[this] var hobby: String = "game" | } defined class Person scala> class Student extends Person { | def sayHello = println("Hello, " + name) | def makeFriends(s: Student) { | println("my hobby is " + hobby + ", your hobby is " + s.hobby) | } | } <console>:15: error: value hobby is not a member of Student println("my hobby is " + hobby + ", your hobby is " + s.hobby) 

eg2:

scala> class Person { | protected var name: String = "leo" | protected var hobby:String = "game" | } defined class Person scala> class Student extends Person { | def sayHello = println("Hello, " + name) | def makeFriends(s: Student) { //this s is not be current instance object | println("my hobby is " + hobby + ", your hobby is " + s.hobby) | } | } defined class Student 
 

4.調用父類的constructor

Scala中,每個類可以有一個主constructor和任意多個輔助constructor,而每個輔助constructor的第一行都必須是調用其他輔助constructor或者是主constructor;因此子類的輔助constructor是一定不可能直接調用父類的constructor的 
只能在子類的主constructor中調用父類的constructor,以下這種語法,就是通過子類的主構造函數來調用父類的構造函數 
注意!如果是父類中接收的參數,比如name和age,子類中接收時,就不要用任何val或var來修飾了,否則會認為是子類要覆蓋父類的field

 
  1. class Person(val name: String, val age: Int)
  2. class Student(name: String, age: Int, var score: Double) extends Person(name, age) {
  3. def this(name: String) {
  4. this(name, 0, 0)
  5. }
  6. def this(age: Int) {
  7. this("leo", age, 0)
  8. }
  9. }
 

5.匿名內部類

在Scala中,匿名子類是非常常見,而且非常強大的。Spark的源碼中也大量使用了這種匿名子類。 
匿名子類,也就是說,可以定義一個類的沒有名稱的子類,並直接創建其對象,然后將對象的引用賦予一個變量。之后甚至可以將該匿名子類的對象傳遞給其他函數。

 
  1. class Person(protected val name: String) {
  2. def sayHello = "Hello, I'm " + name
  3. }
  4. val p = new Person("leo") {//p is a anonymity child class's object
  5. override def sayHello = "Hi, I'm " + name
  6. }
  7. def greeting(p: Person { def sayHello: String }) {
  8. println(p.sayHello)
  9. }
  10. greeting(p)//use anonymity class's object as a method parameter
 

6.抽象類 abstract class and abstract field

如果在父類中,有某些方法無法立即實現,而需要依賴不同的子類來覆蓋,重寫實現自己不同的方法實現。此時可以將父類中的這些方法不給出具體的實現,只有方法簽名,這種方法就是抽象方法。

而一個類中如果有一個抽象方法,那么類就必須用abstract來聲明為抽象類,此時抽象類是不可以實例化的 
在子類中覆蓋抽象類的抽象方法時,不需要使用override關鍵字

 
  1. abstract class Person(val name: String) {
  2. def sayHello: Unit
  3. }
  4. class Student(name: String) extends Person(name) {
  5. def sayHello: Unit = println("Hello, " + name)
  6. }

如果在父類中,定義了field,但是沒有給出初始值,則此field為抽象field 
抽象field意味着,scala會根據自己的規則,為var或val類型的field生成對應的getter和setter方法,但是父類中是沒有該field的 
子類必須覆蓋field,以定義自己的具體field,並且覆蓋抽象field,不需要使用override關鍵字

 
    1. abstract class Person {
    2. val name: String
    3. }
    4. class Student extends Person {
    5. val name: String = "leo"
    6. }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM