1.協變返回類型
- 表示子類中被覆蓋的方法可以返回父類方法的返回類型的某種子類類型。
class Grain { public String toString() { return "Grain"; } } class Wheat extends Grain { public String toString() { return "Wheat"; } } class Mill { Grain process() { return new Grain(); } } class WheatMill extends Mill { /** * 重寫父類的process方法,可以看到返回類型改成了Grain的子類了,它是一個新方法嗎,不是, * 這就是協變返回類型,覆蓋方法可以返回父類返回方法類型的子類 在這裏 * WheatMill是Mill的子類,process()爲WheatMill重寫Mill的方法, * Mill中的返回類型是Grain,WheatMill的返回類型是Wheat,Wheat是Grain的子類 * * Java SE5 之前的版本將強制process的覆蓋方法返回Grain,而不能返回其子類型Wheat, * 協變類型可以 */ Wheat process() { return new Wheat(); } } public class CovariantReturn { public static void main(String[] args) { Mill m = new Mill(); // 這兒的g是Grain類型的引用,而mill的process方法返回的也是Grain對象 Grain g = m.process(); System.out.println(g); m = new WheatMill(); g = m.process(); System.out.println(g); } }
2.is-a和is-like-a
- is-a 是一個的關係:這是一種純粹的繼承關係,即父類中有多少方法,子類中重寫多少方法,不能自己擴展方法。
- is--like-a 像是一個的關係:可以重寫相應的方法,也可以擴展相應的一些方法,比較靈活。
- is-like-a的關係有一個問題,比如List list = new ArrayList(); 那麼list這樣一個引用只能調用List中有的,ArrayList中有的或沒有的方法,不能調用ArrayList中擴展的方法。
這種情況下必須知道確切的類型才能訪問ArrayList中所擴充的方法。也就引出了下面的向下轉型。
3.向下轉型與運行時類型識別
- 與向上轉型相反,是往繼承圖向下移動。
- 向上轉型會丟失方法,而向下轉型會獲得方法
- JAVA 是重類型的語言,所有的轉型都會得到檢查,即使進行一次普通的括弧形式的類型轉換,在進入運行期是仍然會對其進行檢查,以便確保得到我們希望的那種類型。
否則就會拋出ClassCastException 異常。
父類引用訪問子類的擴展方法,必須進行向下轉型才能獲取到。/** * is-like * is-like-a * 向下轉型 * @date 2016-8-20 下午12:28:32 */ class Useful{ public void f(){} public void g(){} } class MoreUseful extends Useful{ public void f(){} public void g(){} public void u(){} public void v(){} public void w(){} } public class RTTI { public static void main(String[] args) { Useful[] x = { new Useful(), new MoreUseful() }; x[0].f(); //向下轉型 獲取方法 ((MoreUseful) x[1]).v(); } }
總結
- 用繼承表達行爲間的差異(各個子類是不同的),用字段表示狀態上的變化()。
- 多態可能伴隨着繼承的存在,但也可能是public 方法 或protect 方法 因爲多個類中可能有相同的public方法,這就需要運行時判斷是調用哪一個類中的方法了。但大多數是繼承。
- 多態是動態綁定:即在程序運行期間方法調用與方法所在類中的主體相關聯並執行其中代碼,在運行前無法知道這個方法在哪個類中。
- 多態是一種不能單獨來看待的特性,相反它只能作爲類關係“全景”中的一部分,與其他部分協同工作。
- 少用繼承,多用組合,協同作戰。