在討論向上轉(zhuǎn)型和向下轉(zhuǎn)型之前,先復(fù)習下多態(tài)运嗜。如果有錯誤的地方,希望能夠指出反惕,大家共同進步驮宴!
什么是多態(tài)雏掠?
多態(tài)(Polymorphism)即同一操作作用于不同的對象中鼠,可以有不同的解釋援雇,產(chǎn)生不同的結(jié)果。
多態(tài)的分類
多態(tài)一般分為兩種:
-
重載式多態(tài)
- 重載式多態(tài)椎扬,也叫編譯時多態(tài)惫搏。也就是說這種多態(tài)在編譯時已經(jīng)確定好了筐赔。方法名相同而參數(shù)列表不同的一組方法就是重載揖铜。在調(diào)用這種重載的方法時汰规,通過傳入不同的參數(shù)最后得到不同的結(jié)果餐茵。
-
重寫式多態(tài)
- 重寫式多態(tài)述吸,也叫運行時多態(tài)忿族。這種多態(tài)通過動態(tài)綁定(dynamic binding)技術(shù)來實現(xiàn),是指在執(zhí)行期間判斷所引用對象的實際類型朴读,根據(jù)其實際的類型調(diào)用其相應(yīng)的方法屹徘。也就是說,只有程序運行起來衅金,你才知道調(diào)用的是哪個子類的方法噪伊。這種多態(tài)通過函數(shù)的重寫以及向上轉(zhuǎn)型來實現(xiàn)。
多態(tài)實現(xiàn)的必要條件
-
繼承或?qū)崿F(xiàn)接口
- 在多態(tài)中必須存在有繼承關(guān)系的子類和父類或者接口及其實現(xiàn)類
-
重寫
- 子類對父類中某些方法進行重新定義氮唯,再調(diào)用這些方法時就會調(diào)用子類的方法
-
向上轉(zhuǎn)型
- 父類引用指向子類對象叫做向上轉(zhuǎn)型鉴吹。
向上轉(zhuǎn)型與向下轉(zhuǎn)型
向上轉(zhuǎn)型,上面已經(jīng)說了惩琉,向下轉(zhuǎn)型是子類引用指向父類對象(父類型豆励,實例是子類的實例化),通常需要進行強制類型轉(zhuǎn)換
案例
//水果類瞒渠,擁有一個show()方法
class Fruits {
public void show() {
System.out.println("我水果之父良蒸,打錢!");
}
}
// 蘋果類,實現(xiàn)父類水果伍玖,并重寫show()方法
class Apple extends Fruits {
@Override
public void show() {
System.out.println("我蘋果嫩痰,打錢!");
}
public void color() {
System.out.println("我是紅色的蘋果。");
}
}
// 香蕉類窍箍,實現(xiàn)父類水果串纺,并重寫show()方法
class Banana extends Fruits {
@Override
public void show() {
System.out.println("我香蕉,打錢!");
}
public void color() {
System.out.println("我是黃色的香蕉椰棘。");
}
}
//測試類
public class Test {
public static void main(String[] args) {
//向上轉(zhuǎn)型
Fruits fruit = new Apple();
fruit.show();
fruit = new Banana();
fruit.show();
}
}
/**
運行結(jié)果:
我蘋果纺棺,打錢!
我香蕉,打錢!
*/
這就是向上轉(zhuǎn)型晰搀,Fruits fruit = new Apple();
將子類對象Apple轉(zhuǎn)化為父類對象Fruits五辽,這個時候fruit引用指向的是子類對象,所以調(diào)用的方法是子類方法外恕。
向上轉(zhuǎn)型的好處
- 減少重復(fù)代碼
- 提高代碼擴展性
向上轉(zhuǎn)型需要注意的問題
向上轉(zhuǎn)型時杆逗,子類單獨定義的方法會丟失。比如鳞疲,上面案例中的Apple類和Banana類都定義了自己的color方法罪郊,當進行了向上轉(zhuǎn)型后,fruit引用指向Apple類的實例時是訪問不到color方法的尚洽,fruit.color()
會報錯悔橄。
向下轉(zhuǎn)型
子類引用指向父類對象(父類型,實例是子類的實例化)腺毫,通常需要進行強制類型轉(zhuǎn)換癣疟,但是這里有個需要注意的問題。
//還是上面的Fruits潮酒、Apple睛挚、Banana
Fruits fruit = new Apple(); //向上轉(zhuǎn)型
Apple apple = (Apple)fruit; //向下轉(zhuǎn)型,強制類型轉(zhuǎn)換
apple.color(); //輸出:我是紅色的蘋果急黎。
Banana banana = (Banana) fruit; //報錯:java.lang.ClassCastException
Fruits f1 = new Fruits();
Apple a1 = (Apple) f1; //報錯:java.lang.ClassCastException
為什么Apple apple = (Apple)fruit;
沒有報錯可以轉(zhuǎn)換成功呢扎狱?因為apple本身就是Apple對象,所以理所當然可以向下轉(zhuǎn)型為Apple勃教,所以自然也就不能轉(zhuǎn)換成Banana淤击,人可以干出指鹿為馬的事情,但是編譯器不行故源,不會指著蘋果說是香蕉污抬。
而f1是Fruits對象,它也不能被向下轉(zhuǎn)型為任何子類對象绳军,就好比你買了一個不知名的水果印机,你只知道它是一種水果,但是你不能直接說這個水果是蘋果或者香蕉删铃。
向下轉(zhuǎn)型需要注意的問題
- 向下轉(zhuǎn)型的前提是父類引用指向的是子類對象耳贬,也就是說,向下轉(zhuǎn)型之前猎唁,它得先進行過向上轉(zhuǎn)型咒劲。
- 向下轉(zhuǎn)型只能轉(zhuǎn)型為本類對象(蘋果是不能變成香蕉的)。
多態(tài)的經(jīng)典案例
簡單的多態(tài)和轉(zhuǎn)型诫隅,相信大家都會腐魂,最后來個復(fù)雜的,一個經(jīng)典案例:
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{
}
class D extends B{
}
public class Demo {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
/**
輸出結(jié)果:
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
*/
結(jié)果1逐纬、2蛔屹、3還好理解,從4開始就糊涂了豁生,4為什么不是“B and B”呢兔毒?
這里我們先看一句話:當父類對象引用變量引用子類對象時漫贞,被引用對象的類型決定了調(diào)用誰的成員方法,但是這個被調(diào)用的方法必須是在父類中定義過的育叁,也就是說被子類覆蓋的方法迅脐。這句話對多態(tài)進行了一個概括,其實在繼承中對象方法的調(diào)用存在一個優(yōu)先級:this.show(O)豪嗽、super.show(O)谴蔑、this.show((super)O)、super.show((super)O)龟梦。
現(xiàn)在來分析一下4隐锭,a2.show(b)
:
- a2是A類型的引用變量,this就代表了A计贰,但是a2所引用的是B對象(new B())钦睡,那么按照上面那句話的意思,
a2.show(b)
就應(yīng)該要調(diào)用B類中的show(B obj)
蹦玫,產(chǎn)生的結(jié)果就是”B and B“赎婚,但是運行結(jié)果明顯不是,別忘了上面的話后面還有一句樱溉,必須是父類中定義過的方法才行挣输,然而B的父類A中并沒有show(B obj)
,難道上面的話是錯誤的福贞?其實不是撩嚼,因為它仍然要按照繼承鏈中調(diào)用方法的優(yōu)先級來確定,所以它才會在A類中找到show(A obj)
挖帘,由于B中重寫了該方法完丽,所以才會調(diào)用B類中的方法,所以得到的結(jié)果是B and A
拇舀。 - 上面的解釋可能有點繞逻族,很多大佬在博客中也是這么寫的,現(xiàn)在我來說說自己的理解骄崩。
A a2 = new B();
這行代碼進行了向上轉(zhuǎn)型聘鳞,前面說過向上轉(zhuǎn)型之后,子類單獨定義的方法會丟失(不能被調(diào)用)要拂,那么這個時候a2可以調(diào)用的方法就剩下A類中的show(D obj)抠璃、show(A obj)
以及B類中的show(A obj)
,那么按照繼承鏈中調(diào)用方法的優(yōu)先級來判斷脱惰,先到類A中找到show(A obj)
搏嗡,由于B類中進行了重寫 ,所以調(diào)用的是B類中的show(A obj)
拉一。
那么按照我的理解采盒,來分析下5旧乞,a2.show(c)
。
- 首先
A a2 = new B();
進行向上轉(zhuǎn)型纽甘,那么良蛮,a2能調(diào)用的方法還是A類中的show(D obj)抽碌、show(A obj)
以及B類中的show(A obj)
悍赢,按照繼承鏈中調(diào)用方法的優(yōu)先級,a2是A類型的引用變量货徙,所以繼承鏈方法調(diào)用優(yōu)先級中this.show(O)
的this就代表了A左权,顯然A類中的方法不滿足這個要求,跳過痴颊,所以接下來是super.show(O)
赏迟,A類沒有父類(除了Object類),再次跳過蠢棱,然后是this.show((super)O)
锌杀,C繼承于B,B繼承于A泻仙,所以show(A obj)
滿足要求糕再,由于a2變量引用的對象類型是B類型,而B類型又重寫了該方法玉转,所以最終調(diào)用的是B類中的show(A obj)
突想,所以最后輸出為B and A
。
能夠分析出4和5究抓,那么接下來的就很簡單了猾担,看到這篇文章的話,可以自己試著分析下刺下。如果有錯誤的地方绑嘹,希望能夠指出,大家共同進步橘茉!
如果對你有幫助工腋,點擊鏈接,關(guān)注一下吧捺癞!