服飾公司的老闆決定要擴大公司的產品線,除了衣服之外,還要販賣帽子、短襪以及褲子等。他意識到這樣可能要改變您先前所發展的銷售系統。
您先前所開發的系統都是銷售衣服,您也許已經定義了Clothes 類別,當中包括了尺寸、價格等資訊,然而即將販賣的帽子、短襪以及褲子等都是「衣服類」,它們同樣擁有尺寸、價格等資訊。
如果您的程式是使用 Java 物件導向程式語言來開發,很幸運的,您可以使用繼承的機制輕易的擴充您的系統,您只要為帽子、短襪以及褲子等定義類別,並讓它們直接繼承Clothes 類別,就可以擁有原來 Clothers 類別中所定義的特性。
在 Java 語言中,使用 extends 關鍵字來表示此類別繼承自其它類別。為了宣告類別為某個類別的子類別,在您的類別宣告就必須使用底下的語法:
[class_modifier] class class_identifier extends superclass_identifier
其中:
以實際的例子來進行說明,假設您的系統中原先已有定義 Clothes 類別,如程式碼 11-9 所示:
public class Clothes { private char size; private double price; public void setSize(char size) { this.size = size; } public char getSize() { return size; } public void setPrice(double price) { this.price = price; } public double getPrice() { return price; } }程式碼 11-9 Clothes.java
現在您打算增加一個褲子產品,褲子也算是衣服的一類,也同樣具有尺寸與價格的屬性,但要多一個腰圍大小,您可以直接繼承 Clothes 類別來定義 Pants,並增加一個 waistline 屬性,如程式碼 11-10 所示:
public class Pants extends Clothes { private int waistline; public void setWaistline(int waistline) { this.waistline = waistline; } public int getWaistline() { return waistline; } }程式碼 11-10 Pants.java
Pants 類別繼承了 Clothes 類別的 size、price 資料成員,並增加了自己的 waistline 成員,而 Clothes 類別中的公開方法也被繼承了下來,並增加了 setWaistline()、getWaistline()兩個公開方法,您可以依同樣的方式,為您的系統增加 Hat、Skirt 等類別。
在名稱上,Clothes 稱之為父類別( Parent class )或基礎類別( Base class ),而 Pants 則稱之為子類別( Child class )或衍生類別( Derived class )。
在繼承了 Clothes 類別之後,對於 Clothes 中原先公開的( public )成員您可以直接呼叫使用,而對於 Clothes 中原先設定為私用的( private )成員,您無法直接呼叫使用,而必須靠原先 Clothes 中提供的公開操作方法來進行存取,一個例子如程式碼 11-11 所示:
public class PantsDemo { public static void main(String[] args) { Pants pants = new Pants(); // 從父類別繼承下來的方法 pants.setSize('L'); pants.setPrice(599.0); // 新增的方法 pants.setWaistline(28); System.out.println("尺寸:" + pants.getSize()); System.out.println("價格:" + pants.getPrice()); System.out.println("腰圍:" + pants.getWaistline()); } }程式碼 11-11 PantsDemo.java
可以看到繼承的好處,在 Pants 中並沒有自行定義setSize()、setPrice()等方法,它仍可以直接使用從 Clothes 繼承下來的這些方法,程式碼 11-11 的執行結果如下所示:
在父類別中的私用(private)成員在繼承後,無法於子類別中直接呼叫使用,如果您希望某成員在被繼承之後,子類別可以直接呼叫使用,則您可以將該成員設定為 protected,一個 protected 成員在繼承後,可以於子類別中直接呼叫使用,但對外部物件仍是保護狀態,不讓外部物件直接呼叫。
例如可以將程式碼 11-9 修改為程式碼 11-12 的內容,將兩個資料成員修改為 protected 權限:
public class Clothes1 { protected char size; protected double price; public void setSize(char size) { this.size = size; } public char getSize() { return size; } public void setPrice(double price) { this.price = price; } public double getPrice() { return price; } }程式碼 11-12 Clothes1.java
如果有個 Skirt 類別繼承了 Clothes1 類別,則它可以在類別中直接存取 size 與 price 成員,而不必再透過公開的方法來存取。
在繼承了父類別之後,如果您對於父類別中的某些方法並不滿意,則您可以在繼承之後重新定義( Override )該方法,或稱之為改寫。舉個例子來說,對於程式碼 11-12 的 setSize() 與 setPrice()方法,為了避免銷售人員輸入錯誤,您也許會希望在繼承它之後重新定義,加入一些簡單的判斷,如程式碼 11-13 所示:
public class Pants1 extends Clothes1 { protected int waistline; // 重新定義 setSize() public void setSize(char size) { if (size != 'L' && size != 'M' && size != 'S') { this.size = 'N'; } else { this.size = size; } } // 重新定義 setPrice() public void setPrice(double price) { if (price < 0.0) { price = 0.0; } else { this.price = price; } } public void setWaistline(int waistline) { this.waistline = waistline; } public int getWaistline() { return waistline; } }程式碼 11-13 Pants1.java
這麼一來,當您在操作 Pants1 型態的物件時,所執行的就是您在 Pants1 中所重新定義的 setSize()與 setPrice()方法,例如:
public class Pants1Demo { public static void main(String[] args) { Pants1 pants1 = new Pants1(); // 呼叫的是重新定義的方法 pants1.setSize('X'); pants1.setPrice(-100.0); // 新增的方法 pants1.setWaistline(28); System.out.println("尺寸:" + pants1.getSize()); System.out.println("價格:" + pants1.getPrice()); System.out.println("腰圍:" + pants1.getWaistline()); } }程式碼 11-14 Pants1Demo.java
在程式碼中的第 5 行、第 6 行故意輸入了錯誤的資料,依Pants1 類別中重新定義的 setSize()與 setPrice()之定義,會將 size 與 price 設定為'N'與 0.0,執行結果如下所示:
重新定義方法時要注意的是,在繼承之後您可以於子類別中增大原先父類別已有的方法權限,但不可以縮小權限,也就是說原來父類別中如果是 public 的方法,則您不可以於重新定義時使用 protected 或 private 修飾,否則會出現以下的錯誤訊息:
...attempting to assign weaker accessprivileges;...
在繼承某類別之後,如果您打算呼叫父類別中原先已定義的建構式或方法,則您可以使用 super 關鍵字,例如,如果父類別是這麼定義:
public class Parent { public Parent(String name) { // .... } }
則繼承了 Parent 類別之後,您想要在建構子類別實例時,先執行 Parent 類別中的建構式,則您可以這麼撰寫:
public class Child extends Parent { public Child(String parentName, String givenName) { super(parentName); // .... } }
如果您在繼承了父類別之後,重新定義父類別中繼承下來的某個方法,而您也許又想執行原來父類別中定義的方法,則您也可以使用 super 來進行呼叫,例如若原先父類別中定義了 someMethod()方法:
public class Parent { public void someMethod() { System.out.println("someMethod"); } }
您繼承了 Parnet 類別後,可以使用 super 關鍵字加上 .運算子,之後接上方法名稱來呼叫原先父類別中的方法,例如:
public class Child extends Parent public void someMethod() { super.someMethod(); System.out.println("someMethod again"); } }
如果您實例化 Child 類別,並呼叫 someMethod()方法的話,則結果會於螢幕上顯示一行 "someMethod" 與一行"someMethod again"的文字。
如果您在設計類別的時候,有個方法並不想讓子類別繼承後重新定義,則您可以在方法上加上"final",例如:
public class Parent { public final void someMethod() { // ... } }
繼承 Parent 類別的子類別可以使用 someMethod(),但無法重新定義 someMethod()。
如果您希望某個類別完全不能被繼承,則可以在定義類別時加上"final"關鍵字,例如:
public final class SomeClass { // }
被 final 修飾的類別無法被繼承,在 Java SE 中的一個實際例子就是 java.lang.String 類別,您無法繼承 String 類別來定義您自己的子類別。