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