scala-面向對象編程之繼承

extends

scala中,讓子類繼承父類,與java一樣,也是使用extends關鍵字。

繼承就代表子類可以從父類繼承父類的field和method;然後子類可以在自己內部放入父類所沒有,子類特有的field和method;使用繼承可以有效複用代碼。

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

class Person {
private var name = "wei"
def getName = name
}

class Student extends Person {
private var score = "A"
def getScore = score
}

scala> val s = new Student
s: Student = Student@f9ac719

scala> s.getName
res6: String = wei

scala> s.getScore
res7: String = A

override 和 super

scala中,如果子類要覆蓋父類中的非抽象方法,則必須使用override關鍵字。override關鍵字可以幫助我們儘早的發現代碼裏的錯誤,比如:override修飾的父類方法的方法名我們拼寫錯了;比如要覆蓋的父類方法的參數我們寫錯了等待。

此外,在子類覆蓋父類的方法之後,如果我們在子類中就是要調用父類的被覆蓋的方法呢?那就可以使用super關鍵字,顯示地指定要調用的父類方法。

class Person {
private var name = "wei"
def getName = name
}
class Student extends Person {
private var score = "A"
def getScore = score
override def getName = "Hi ,I'm" + super.getName
}

// Exiting paste mode, now interpreting.

defined class Person
defined class Student

scala> var s = new Student
s: Student = Student@632a7680

scala> s.getName
res8: String = Hi ,I'mwei

override field

scala中,子類可以覆蓋父類的val field,而且子類的val field可以覆蓋父類的val field的getter方法;只要在子類中使用override關鍵字即可

class Person {
val name: String = "person"
def age: Int = 0
}

class Student extends Person {
override val name: String = "leo"
override val age: Int = 30
}

scala> val p = new Person
p: Person = Person@2bac4423

scala> p.name
res9: String = person

scala> p.age
res10: Int = 0

scala> val s = new Student
s: Student = Student@7c87c960

scala> s.name
res11: String = leo

scala> s.age
res12: Int = 30

isInstanceOf 和 asInstanceOf

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

首先用isInstanceOf 判斷對象是否是指定類型的對象,如果是的話,可以使用asInstanceOf將對象轉化爲指定類型。

注意,如果對象是null,則isInstanceOf一定返回false, asInstanceOf 一定返回null
注意,如果沒有用isInstanceOf先判斷對象是否爲指定類的實例,就直接使用asInstanceOf轉換,則可能會拋出異常。

class Person
class Student extends Person

scala> val p:Person = new Student
p: Person = Student@45571cc9

scala> var s:Student = null
s: Student = null

scala> 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

scala> p.isInstanceOf[Person]
res15: Boolean = true

scala> p.getClass == classOf[Person]
res16: Boolean = false

scala> p.getClass == classOf[Student]
res17: Boolean = true

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

在實際開發中,比如spark的源碼中,大量地方都是使用模式匹配的方式進行類型判斷,這種方式更加地簡潔明瞭,而且代碼的可維護性和可擴展性也非常高

使用模式匹配,功能性上來說,與isInstanceOf一樣,也是判斷只要是該類以及該類的子類的對象即可,不是精準判斷的

class Person
class Student extends Person

val p:Person = new Student

p match{
case per:Person => println("it's Person's object")
case _ => println("unknown type")

it's Person's object
}

protected

跟java一樣,scala中同樣可以使用protected關鍵字來修飾field和method,這樣在子類中就不需要super關鍵字,直接就可以訪問到field和method

還可以使用protected[this],則只能在當前子類對象中訪問父類的field和method,無法通過其他子類對象訪問父類的field和method

class Person {
protected var name:String = "wei"
protected[this] var hobby:String = "game"
}
class Student extends Person{
def sayHello = println("Hello, " + name)
def makeFriends(s:Student){
println("my hobby is " + hobby + ",your hobby is " + s.hobby)
}
}

<console>:20: error: value hobby is not a member of Student
       println("my hobby is " + hobby + ",your hobby is " + s.hobby)
                                                              

以上代碼會報錯,因爲hobby用protected[this]修飾,無法通過其他子類對象訪問。

調用父類的constructor

scala中,每個類可以有一個主constructor和任意多個輔助constructor,而每個輔助constructor的第一行都必須是調用其他輔助constructor或者是主constructor;因此子類的輔助constructor是一定不可能直接調用父類的constructor的

只能在子類的主constructor中調用父類的constructor,以下這種語法就是通過子類的主constructor調用父類的constructor

注意,如果是父類中接收的參數,比如name和age,子類中接收時,就不要用任何val或var來修飾了,否則會認爲是子類要覆蓋父類的field

class Person(val name:String, val age:Int)
class Student(name:String, age:Int, var score:Double) extends Person(name,age) {
def this(name:String){
this(name,0,0)
}
def this(age:Int) {
this("wei", age, 0)
}
}

val s = new Student("wei", 0, 0)

匿名內部類

在scala中,匿名子類是非常常見的,而且非常強大的,spark的源碼中也大量使用了這種匿名子類。

匿名子類,也就是說,可以定義一個類的沒有名稱的子類,並直接創建其對象,然後將對象的引用賦予一個變量,之後甚至可以將該匿名子類的對象傳遞給其他函數。

class Person(protected val name:String) {
def sayHello = "Hello,I'm " + name
}
val p = new Person("wei"){
override def sayHello = "Hi,I'm " + name
}
def greeting(p:Person{def sayHello:String}) {
println(p.sayHello)
}

greeting(p)

抽象類

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

而一個類中如果有一個抽象方法,那麼類不許用abstract來聲明爲抽象類,此時抽象類是不可以實例化的

在子類中覆蓋抽象類的抽象方法時,不需要使用override關鍵字

abstract class Person(val name:String) {
def sayHello:Unit
}
class Student(name:String) extends Person(name) {
def sayHello:Unit = println("Hello," + name)
}

抽象field

如果在父類中,定義了field,但是沒有給出初始值,則此field爲抽象field

抽象field意味着,scala會根據自己的規則,爲val或var類型的field生成對應的getter和setter方法,但是父類中是沒有該field的

子類必須覆蓋filed,以定義自己的具體field,並且覆蓋抽象field,不需要使用override關鍵字

abstract class Person {
val name:String
}
class Student extends Person {
val name:String = "wei"
}
scala> val s = new  Student
s: Student = Student@22175d4f

scala> s.name
res6: String = wei

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章