class Useful
{
int value = 20;
public void f() {System.out.println("f() in the Useful class");}
public void g() {System.out.println("g() in the Useful class");}
}
class MoreUseful extends Useful {
int value = 21;
public void f() {System.out.println("f() in the MoreUseful class");}
public void g() {System.out.println("g() in the MoreUseful class");}
public void u() {System.out.println("u() in the MoreUseful class");}
public void v() {}
public void w() {}
}
class MoreUseful2 extends Useful {
int value = 21;
public void f() {System.out.println("f() in the MoreUseful2 class");}
public void g() {System.out.println("g() in the MoreUseful2 class");}
public void u() {System.out.println("u() in the MoreUseful2 class");}
public void v() {}
public void w() {}
}
class ExtendsMoreUseful2 extends MoreUseful2 {
int value = 22;
public void f() {System.out.println("f() in the ExtendsMoreUseful2 class");}
public void g() {System.out.println("g() in the ExtendsMoreUseful2 class");}
public void u() {System.out.println("u() in the ExtendsMoreUseful2 class");}
public void v() {}
public void w() {}
}
public class RTTI {
public static void main(String[] args) {
//Useful useful = new MoreUseful();
//useful.u(); //這是錯誤的,u()方法沒有在useful類型中調用。
Useful[] x =
{
new Useful(),
new MoreUseful(),
new MoreUseful2(),
new ExtendsMoreUseful2()
}; //聲明一個變量數組,保存每個Useful對象的引用,
//那么每一個Useful對象就會自動向上轉型為Useful。
for(int i=0;i<x.length;i++)
{
if(x[i] instanceof MoreUseful2) //判斷instanceof左邊的對象是否是右邊的類的實例。
{
MoreUseful2 moreuseful2 = (MoreUseful2)x[i];//向下轉型(具體解釋見下面的分析)
moreuseful2.u();
}
x[i].g(); //這是動態綁定,將方法的調用和方法主體關聯起來就是動態綁定。
}
運行結果:
g() in the Useful class // 由于x[0].g()這一句中調用g()方法實際的對象類型是
// 基類Useful類型,故將調用基類中的f()方法。(沒有轉型)
g() in the MoreUseful class // 由于x[1].g()這一句中調用g()方法實際的對象類型是
//子類Moreuseful類型,故將調用子類Moreuseful中
// 覆蓋父 類的g()方法。(向上轉型)
u() in the MoreUseful2 class // 由于x[2]是MoreUseful2類型,故可以對其向下轉型調
// 用MoreUseful2中的擴展方法u()。(向下轉型)
g() in the MoreUseful2 class // 又x[2].g()這一句中調用g()方法實際的對象類型是子
// 類Moreuseful類型,故將調用子類Moreuseful中
// 覆蓋父類的g()方法。(向上轉型)
u() in the ExtendsMoreUseful2 class // 由于x[3]是ExtendMoreUseful2類型,
// 它是MoreUseful2的子類,存在is-a關系,故可以對其向下轉
// 型,將調ExtendMoreUseful2中的擴展的方法u()。(向下轉型)
g() in the ExtendsMoreUseful2 class // 又x[3].g()這一句中調用g()方法實際的對象類型
//是子類ExtendMoreuseful類型故將調用
// 子類ExtendMoreuseful中覆蓋父類的g()方法。(向上轉型)
分析和結論:
(一)向上轉型
(1)定義: 把對某個對象的引用視為對其基類引用的做法被稱為“向上轉型”。
這主要是由于子類的對象可以看成是基類的對象這原因而得來的,也就是具有is-a關系。
比如:
Useful useful = new MoreUseful();//右邊是一個子類的對象,而左邊是一個父類類型
//的變量,指向右邊的子類對象。
(2)基類可以接收發給導出類的任何消息,因為二者有完全相同的接口,我們只需要
從導出類向上轉型,永遠不需要知道正在處理的對象的確切類型,這也就是多態性決
定的。利用多態性,具有同樣方法名和方法特征的方法根據調用方法的對象的類型,
可以產生不同的動作,這極大地增加了程序員的表達能力。
回頭再看一看上面這個例子中的一段for循環代碼,
for(int i=0;i<x.length;i++)
{
if(x[i] instanceof MoreUseful2) //判斷instanceof左邊的對象是否是右邊的類的實例。
{
MoreUseful2 moreuseful2 = (MoreUseful2)x[i]; //向下轉型(具體解釋見下面的分析)
moreuseful2.u();
}
x[i].g(); //動態綁定
}
主要看x[i].g();這一句話,現在我們還不知道x[i]這個到底是指代哪一個Useful對象,
在這種情況下,編譯器是怎么知道調用哪個方法的呢?這是一個動態綁定的問題,見
下面。
(二)動態綁定
(1)定義:將方法的調用和方法主體關聯起來就是動態綁定。
比如:x[i].g();這就是一個動態綁定,x[i]是一個對象類型,g()是一個不知道是屬于
哪個對象的方法,將這兩個兩個聯合在一起,就是一個綁定。
(2)要注意:Java中除了static和final方法(private方法屬于final方法,因為fianl方法
不可以覆蓋,static方法是一個全局方法,屬于所有類共享,不在多態范圍內)之外,
其他所有的方法都是動態綁定,這就意味著在通常情況下,我們不必判定是否應該進
行動態綁定,因為這個會自動發生。
(3)接著上面的答復。編譯器是怎么知道調用哪個方法的呢?
主要還是x[i].g();這一句話的作用,x[i]在調用方法的時候,會調用“實際”的方法,這
個實際的方法由所引用的對象的類型決定。那么調用的實際方法可能是父類中沒有被
子類覆蓋的方法,也可能是子類中覆蓋父類的方法,主要看調用這個方法的實際的對
象類型是哪一個。
(三)向下轉型
既然有向上轉型,那么有沒有向下轉型呢?
答:有
(1)概述
繼承可以確保所有的子類類具有基類的接口,且絕對不會少。那么子類除了有父類的
方法,也可以有自己的額外的新方法(這些方法是基類所沒有的),那么一旦向上轉
型,就不能調用子類中的新方法,那么能不能用一種方式調用這些新方法呢?當然有
了,這時候就需要向下轉型。
(2)向下轉型
將超類的引用強制轉換為子類類型就叫做向下轉型。
注意:將超類的引用賦給為子類類型的變量(沒有進行顯示地強制轉換)是一個編譯
錯誤。
例子:
還是上面的for循環代碼
for(int i=0;i<x.length;i++)
{
if(x[i] instanceof MoreUseful2) // 判斷instanceof左邊的對象是否是右邊的類的實例。
{
MoreUseful2 moreuseful2 = (MoreUseful2)x[i]; // 向下轉型
moreuseful2.u();
}
x[i].g();
}
分析:x[i]可以代表具體的Useful對象類型,當它是MoreUseful2或ExtendsMoreUseful2
對象類型時,就可以調用該對象的額外方法u(),v(),w(),也就是當對象x[i]和Moreusful對
象存在is-a關系時,才可以進行向下轉型,如果要轉換的對象類型與指定的對象類型不
存在is-a關系時,會產生一個ClassCastException異常。
總之:
向下轉型時,對象只能強制轉換為其本身類型或者其超類類型。比如,
當x[i]ExtendsMoreUseful2對象時,可以把他轉換為其本身ExtendsMoreUseful2對象類
型,也可以把它轉換為其基類MoreUseful2類型。但是在編譯時候還不知道這個x[i]是代
表那個具體對象類型只知道這個x[i]是基類類型引用,所以要用這樣的形式" (想要要得
到的類型)x[i] " 進行轉換。x[i]在這里是就我這個例子來說明的,你也可以使用其它的
英文代替,其意義是一切符合規定的需要被轉換的對象。
下面還有個關于向上轉型和向下轉型的例子,
abstract class ClassAbstract1{}
class ClassDerived1 extends ClassAbstract1
{
public void play1()
{
System.out.println("play1() is in the ClassDerived1");
}
}
abstract class ClassAbstract2{public abstract void play2();}
class ClassDerived2 extends ClassAbstract2
{
public void play2()
{
System.out.println("play2() is in the ClassDerived2");
}
}
public class E14_UnCast {
public static void playDemo1(ClassAbstract1 ObjectParameter)
{
((ClassDerived1)ObjectParameter).play1();//向下轉型,可以調用導出類中的擴展方法
}
public static void playDemo2(ClassAbstract2 ObjectParameter)
{
ObjectParameter.play2();//向上轉型,可以調用導出類中的覆蓋方法
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ClassAbstract1 classabstract = new ClassDerived1();
playDemo1(classabstract);
ClassAbstract2 classabstract2 = new ClassDerived2();
playDemo2(classabstract2);
}
}
運行結果:
play1() is in the ClassDerived1
play2() is in the ClassDerived2