Scala入門到精通——第九節 繼承與組合
主要內容
- 類的繼承
- 建構函式執行順序
- 方法重寫
- 匿名類
- 多型與動態繫結
- 組合與繼承的使用
1 類的繼承
下類的程式碼演示了scala類的繼承
//Person類
class Person(name:String,age:Int){
}
//Student繼承Person類
class Student(name:String,age:Int,var studentNo:String) extends Person(name,age){
}
object demo{
def main(args: Array[String]): Unit = {
val student=new Student("john",18,"1024")
}
}
程式碼
//Person類
class Person(name:String,age:Int){
}
//Student繼承Person類
class Student(name:String,age:Int,var studentNo:String) extends Person(name,age){
}
等同於下面的Java程式碼
//Person類
class Person{
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
}
//Student繼承Person類
class Student extends Person{
private String studentNo;
public Student(string name,int age,String studentNo){
super(name,age);
this.sutdentNo=studentNo;
}
}
2. 建構函式執行順序
下面的程式碼演示scala在繼承的時候,建構函式的執行順序
//Person類
class Person(name:String,age:Int){
println("Constructing Person")
}
//Student繼承Person類
class Student(name:String,age:Int,var studentNo:String) extends Person(name,age){
println("Constructing Student")
}
object demo{
def main(args: Array[String]): Unit = {
//下面的語句執行時會列印下列內容
//Constructing Person
//Constructing Student
//也就是說,構造Student這前,首先會呼叫Person的主構造方法
val student=new Student("john",18,"1024")
}
}
下面這段程式碼
//Person類
class Person(name:String,age:Int){
println("Constructing Person")
}
//Student繼承Person類
class Student(name:String,age:Int,var studentNo:String) extends Person(name,age){
println("Constructing Student")
}
等同於下面的java程式碼
//Person類
class Person{
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
System.out.println("Constructing Person");
}
}
//Student繼承Person類
class Student extends Person{
private String studentNo;
public Student(string name,int age,String studentNo){
super(name,age);
this.sutdentNo=studentNo;
System.out.println("Constructing Student");
}
}
3. 方法重寫
方法重寫指的是當子類繼承父類的時候,從父類繼承過來的方法不能滿足子類的需要,子類希望有自己的實現,這時需要對父類的方法進行重寫,方法重寫是實現多型和動態繫結的關鍵。
scala中的方法重寫同java一樣,也是利用override關鍵字標識重寫父類的演算法。
下面的程式碼演示了方法重寫如何實現
class Person(name:String,age:Int){
//println("Constructing Person")
def walk():Unit=println("walk like a normal person")
}
//Student繼承Person類
class Student(name:String,age:Int,var studentNo:String) extends Person(name,age){
//println("Constructing Student")
override def walk():Unit={
super.walk()//呼叫父類的walk方法
println("walk like a elegant swan")//增加了自己的實現
}
}
object demo{
def main(args: Array[String]): Unit = {
val s=new Student("john",18,"1024")
s.walk()
}
}
//程式碼執行輸出內容
walk like a normal person
walk like a elegant swan
不得不提的是,如果父類是抽象類,則不override關鍵字可以不加,這是因為如果繼承的父類是抽象類(假設抽象類為AbstractClass,子類為SubClass),在SubClass類中,AbstractClass對應的抽象方法如果沒有實現的話,那SubClass也必須定義為抽象類,否則的話必須要有方法的實現,這樣的話,加不加override關鍵字都是可以的。下面是一個例項程式碼:
//抽象的Person類
abstract class Person(name:String,age:Int){
def walk():Unit
}
//Student繼承抽象Person類
class Student(name:String,age:Int,var studentNo:String) extends Person(name,age){
//重寫抽象類中的walk方法,可以不加override關鍵字
def walk():Unit={
println("walk like a elegant swan")
}
}
object demo{
def main(args: Array[String]): Unit = {
val s=new Student("john",18,"1024")
s.walk()
}
}
4. 匿名類
當某個類在程式中只使用一次時,可以將類定義為匿名類,匿名類的定義如下:
//抽象的Person類
abstract class Person(name:String,age:Int){
def walk():Unit
}
object demo{
def main(args: Array[String]): Unit = {
//下面的程式碼定義了一個匿名類,並且進行了例項化
//直接new Person("john",18),後面跟的是類的內容
//我們知道,Person是一個抽象類,它是不能被例項化的
//這裡能夠直接new操作是因為我們擴充套件了Person類,只不
//過這個類是匿名的,只能使用一次而已
val s=new Person("john",18){
override def walk()={
println("Walk like a normal Person")
}
}
s.walk()
}
}
5 多型與動態繫結
“多型”(Polymorphic)也叫“動態繫結”(Dynamic Binding)、“遲繫結”(Late Binding),指“在執行期間(而非編譯期間)判斷所引用物件的實際型別,根據其實際型別呼叫其相應的方法。”即指子類的引用可以賦給父類,程式在執行時根據實際型別呼叫對應的方法
下面的程式碼演示了scala中的多型與動態繫結:
//抽象Person類
abstract class Person(var name:String,var age:Int){
def walk():Unit
//talkTo方法,引數為Person型別
def talkTo(p:Person):Unit
}
class Student(name:String,age:Int) extends Person(name,age){
private var studentNo:Int=0
def walk()=println("walk like a elegant swan")
//重寫父類的talkTo方法
def talkTo(p:Person)={
println("talkTo() method in Student")
println(this.name+" is talking to "+p.name)
}
}
class Teacher(name:String,age:Int) extends Person(name,age){
private var teacherNo:Int=0
def walk()=println("walk like a elegant swan")
//重寫父類的talkTo方法
def talkTo(p:Person)={
println("talkTo() method in Teacher")
println(this.name+" is talking to "+p.name)
}
}
object demo{
def main(args: Array[String]): Unit = {
//下面的兩行程式碼演示了多型的使用
//Person類的引用可以指向Person類的任何子類
val p1:Person=new Teacher("albert",38)
val p2:Person=new Student("john",38)
//下面的兩行程式碼演示了動態繫結
//talkTo方法引數型別為Person型別
//p1.talkTo(p2)傳入的實際型別是Student
//p2.talkTo(p1)傳入的實際型別是Teacher
//程式會根據實際型別呼叫對應的不同子類中的talkTo()方法
p1.talkTo(p2)
p2.talkTo(p1)
}
}
6. 組合與繼承的使用
繼承可以重用父類的程式碼,從而簡化程式設計,繼承是is-a的關係,apple is a kind of fruit(蘋果是一種水果)。還有一種程式碼重用的方式是組合,組合是has-a的關係(one person has a head)。繼承在前面已經講了,這邊只給出組合的使用程式碼:
class Head
class Body
class Hand
//....
//Person類
abstract class Person(var name:String,var age:Int){
//各類的例項作為該類物件的一部分,通過各類的例項方法實現程式碼重用
val head:Head=null
val body:Body=null
val hadn:Hand=nulll
//....
}
繼承與組合使用總結:
一 繼承
繼承是Is a 的關係,比如說Student繼承Person,則說明Student is a Person。繼承的優點是子類可以重寫父類的方法來方便地實現對父類的擴充套件。
繼承的缺點有以下幾點:
1 父類的內部細節對子類是可見的。
2 子類從父類繼承的方法在編譯時就確定下來了,所以無法在執行期間改變從父類繼承的方法的行為。
3 如果對父類的方法做了修改的話(比如增加了一個引數),則子類的方法必須做出相應的修改。所以說子類與父類是一種高耦合,違背了面向物件思想。
二 組合
組合也就是設計類的時候把要組合的類的物件加入到該類中作為自己的成員變數。組合的優點:
1 當前物件只能通過所包含的那個物件去呼叫其方法,所以所包含的物件的內部細節對當前物件時不可見的。
2 當前物件與包含的物件是一個低耦合關係,如果修改包含物件的類中程式碼不需要修改當前物件類的程式碼。
3 當前物件可以在執行時動態的繫結所包含的物件。可以通過set方法給所包含物件賦值。
組合的缺點:
1 容易產生過多的物件。
2 為了能組合多個物件,必須仔細對介面進行定義。
由此可見,組合比繼承更具靈活性和穩定性,所以在設計的時候優先使用組合。只有當下列條件滿足時才考慮使用繼承:
1 子類是一種特殊的型別,而不只是父類的一個角色
2 子類的例項不需要變成另一個類的物件
3 子類擴充套件,而不是覆蓋或者使父類的功能失效
新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊