繼承 (Inheritance) 是可以大幅度減少重複程式碼的方式,如果開始發現不停的在重複寫或copy-paste 同一段程式碼,也許可以試著先停下來想一想,這些類別有什麼相似的方法與屬性,如果有,那就是繼承發揮功能的時刻了!
現在我們再把水果家族給叫出來吧,然後我們來發想一下,身為這些水果會有什麼屬性與方法呢?
lemon 檸檬:
屬性:重量、顏色、價格、酸度
方法:吃、切、榨汁
grape 葡萄:
屬性:重量、顏色、價格、顆粒數
方法:吃、剝皮
watermelon 西瓜:
屬性:重量、顏色、價格、形狀
方法:吃、切
從上面發現,這些類別有許多項目是重複出現的,但這些重複項目在每個 Class 都必須重新攥寫;此外,如果以重量為例,如果現在想更改重量的單位,那就給全部的類別一個一個去改,相當費時費力。
如果在這裡我們把檸檬、葡萄與西瓜的共同屬性與方法都獨立出來成為一個新的類別叫 fruits,在現實中檸檬、葡萄與西瓜確實也是水果的一種,我們可以讓他們都擁有身為水果該有的屬性與方法,再加上各自專屬的屬性與方法。這種取得水果共有屬性與方法的動作,就叫做繼承(Inheritance)
在這個例子中,我們會稱 fruits 為父類別,lemon、grape 與 watermelon 都「繼承」了身為 fruits 的特性,都稱為子類別。
子類別會擁有父類別所有的屬性與方法,再加入出自己專屬的屬性與方法,可以視為父類別的延伸(Extend)
Java 中要實踐繼承要使用 extends,而且在 Java 中一個子類別只可以繼承一個父類別
,不可以多成繼承。
class 子類別 extends 父類別{
...
}
以前述例子來看,可以建立一個水果的父類別,擁有共同的屬性(重量、顏色與價格)以及共同的方法 (吃):
public class Fruits {
// 擁有共同的屬性: 重量、顏色與價格
public float weight;
public String color;
public int price;
// 建構子 Constructor
Fruits(float weight,String color,int price){
this.weight=weight;
this.color=color;
this.price=price;
}
// 擁有共同的方法:吃
public void eat(){
System.out.println("I'm eating "+ getClass().getName().split("\\.")[1]+".");
return;
}
}
接下來,建立一個葡萄 grape 的子類別,除了要使用 extends 繼承 Fruits 之外,還需要在建構子 (Constructor) 去調用父類別的建構子才能完成繼承。因此,需要用到 super()
函式,super()
相當於父類別的建構子,在這個例子中同等於Fruits()
的作用。
// 透過 extends 繼承了 類別 Fruits
public class Grape extends Fruits{
// 添加葡萄特有的屬性: 顆粒數
public int numbers;
// 建構子 Constructor
public Grape(float weight, String color, int price, int numbers) {
// 以 super() 調用父類別的建構子
super(weight, color, price);
this.numbers=numbers;
}
// 添加葡萄特有的方法: 剝皮
public void peel(){
System.out.println("I'm peeling "+ getClass().getName().split("\\.")[1]+".");
return;
}
}
好了,到這裡我們就完成了葡萄 Grape 這個類別,接著來實作一個物件出來測試看看。
// 建立葡萄物件 Grape(重量, 顏色, 價格, 顆數)
Grape grape = new Grape(250, "green", 180, 35);
那實做出來的葡萄物件具備了重量, 顏色, 價格與顆數四種屬性,可以看到我們沒有在 Grape 中建立重量 、顏色與價格的屬性,但因為繼承 Fruits 的關係,子類別會擁有父類別全部的屬性。
System.out.println(grape.weight); // 250.0
System.out.println(grape.color); // green
System.out.println(grape.price); // 180
System.out.println(grape.numbers); // 35
grape 也擁有 eat() 跟 peel() 兩種方法,其中 eat() 是從 Fruits 繼承而來。
grape.peel();
// I'm peeling Grape.
grape.eat();
// I'm eating Grape.
所以到這裡我們可以看到透過繼承的方式,我們在新增的子類別中,只需要添加該類別專屬的屬性與方法,可以大量減少重複的程式碼。