今天學習了Scala面向對象的繼承,和Java還是有不同的地方,在此總結一下!
一.Scala中,讓子類繼承父類,與Java中一樣,使用extends關鍵字。
繼承就代表,子類可以從父類繼承父類的field和method;然後子類可以在自己内部放入父類所沒有,子類特有的field和method,使用繼承可以有效的複用代碼。
父類如果用final關鍵詞修飾,field和method用final修飾,則該類是無法被繼承的,field和method是無法被覆寫的。
class Person{
private var name = "xiaoming"
def getName = name
}
class Student extends Person{
private var score = 30
def getScore = score
}
執行:
val s = new Student
s.getName
輸出:
xiaoming
二.override和super
Scala中,如果子類覆寫一個父類中非抽象方法,則必須使用override關鍵字。
在子類覆寫父類的方法之後,如果我們在子類中就是要調用父類的被覆寫的方法,可以使用super關鍵字,顯式的調用父類的方法。
1.override method
class Person{
private var name = "xiaoming"
def getName = name
}
class Student extends Person{
private var score = 30
def getScore = score
override def getName = "Hi,my name is " + super.getName
}
執行:
val s = new Student
s.getName
輸出:
Hi,my name is xiaoming
2.override field
Scala中,子類可以覆寫父類的val field,而且子類的val field還可以覆寫父類的val field的getter方法。
class Person{
val name:String = "people"
def age:Int = 0
}
class Student extends Person{
override val name:String = "xiaoming"
override def age:Int = 30
}
執行:
val s = new Student
s.name
輸出:
xiaoming
三.isInstanceOf和asInstanceOf
如果我們建立了子類的對象,但是又将其賦予了父類類型的變量。則在後續的程式中,我們又需要将父類類型的變量轉換為子類類型的變量,該怎麼做?
首先,我們需要使用isInstanceOf判斷對象是否是指定類的對象,如果是的話,則可以使用asInstanceOf将對象轉換為指定類型。
注意:如果對象是null,則isInstanceOf一定傳回false,asInstanceOf一定傳回null。
class Person
class Student extends Person{
val p:Person = new Student //将子類對象賦給父類變量
var s:Student = null
if(p.isInstanceOf[Student]) p.asInstance[Student]
}
四.getClass和classOf
isInstanceOf隻能判斷出對象是否是指定類以及其子類的對象,而不能精确判斷出,對象就是指定類的對象。
對象.getClass可以精确的擷取對象的類,classOf[類]可以精确擷取類,然後使用 == 操作符即可判斷。
scala>class Person
defined class Person
scala>class Student extends Person
defined class Student
scala>val p:Person = new Student
p:Person = Student
scala>p.isInstanceOf[Person]
Boolean = true
scala>p.getClass == classOf[Person]
Boolean = false
scala>p.getClass == classOf[Student]
Boolean = true
五.使用模式比對進行類型判斷
在實際開發中,大多數情況下都使用模式比對的方式來進行類型的判斷,這種方式更加的簡潔明了,而且代碼可維護性和可擴張性也非常高。
使用模式比對,功能性上來說,與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") //_是占位符
}
六.protected
跟Java一樣,Scala中同樣使用protected關鍵字修飾field和method,這樣在子類中就不需要super關鍵字,直接就可以通路field和method。
還可以使用protected[this],則隻能在目前子類對象中通路父類的field和method,無法通過其他子類對象通路父類的field和method。
class Person{
protected var name:String = "leo"
protected[this] var hobby:String = "game"
}
class Student extends Person{
def sayHello = println("Hello, " + name) //不需要super關鍵字就可以通路父類中的name
def makeFriends(s:Student){ //傳入一個其他子類的對象
println("my hobby is " + hobby + ",your hobby is " + **s.hobby**) //程式報錯,無法通過其他子類對象通路父類中的hobby屬性
}
}
七.調用父類的構造函數
Scala中,每個類可以有一個主構造函數和任意多個輔助構造函數,而每個輔助構造函數的第一行都必須是調用其他輔助構造函數或者是主構造函數,是以子類的輔助構造函數是一定不能直接調用父類的構造函數的。
隻能在子類的主構造函數中調用父類的構造函數。
**注意:**如果是父類中接收的參數,比如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){ //輔助構造函數,隻接收一個參數name
this(name,0,0) //輔助構造函數必須首先調用主構造函數
}
def this(age:Int){ //另外一個輔助構造函數,隻接收一個參數age
this("xiaoming",age,0)
}
}
八.匿名子類 (相當于Java中的匿名内部類)
在Scala中,匿名子類很強大。匿名子類,就是說,可以定義一個類的沒有名稱的子類,并直接建立其對象,然後将對象的引用賦予一個變量,之後甚至可以将該匿名子類的對象傳遞給其他函數。
class Person(protected val name:String){ //name用protected修飾,子類可以直接使用
def sayHello = "Hello,i'm " + name
}
val p = new Person("xiaoming"){ //建立匿名子類對象,并将其賦給變量p
override def sayHello = "Hi,i'm " + name //覆寫父類的方法
}
def greeting(p:Person{def sayHello:String}){
println(p.sayHello)
}
九.抽象類
如果在父類中,有某些方法無法立即實作,而需要依賴不同的子類來覆寫,重寫實作自己不同的方法實作,此時可以将父類中的這些方法不給出具體的實作,隻有方法簽名,這種方法就是抽象方法。
而一個類中如果有一個抽象方法,那麼類就必須用abstract來聲明為抽象類,此時抽象類是不可以執行個體化的。
在子類中覆寫抽象類的抽象方法時,不需要使用override關鍵字。
abstract class Person(val name:String){
def sayHello:Unit
}
class Student(name:String) extends Person(name){
def sayHello:Unit = println("Hello " + name)
}