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(); //這是錯(cuò)誤的,u()方法沒(méi)有在useful類(lèi)型中調(diào)用。
Useful[] x =
{
new Useful(),
new MoreUseful(),
new MoreUseful2(),
new ExtendsMoreUseful2()
}; //聲明一個(gè)變量數(shù)組,保存每個(gè)Useful對(duì)象的引用,
//那么每一個(gè)Useful對(duì)象就會(huì)自動(dòng)向上轉(zhuǎn)型為Useful。
for(int i=0;i<x.length;i++)
{
if(x[i] instanceof MoreUseful2) //判斷instanceof左邊的對(duì)象是否是右邊的類(lèi)的實(shí)例。
{
MoreUseful2 moreuseful2 = (MoreUseful2)x[i];//向下轉(zhuǎn)型(具體解釋見(jiàn)下面的分析)
moreuseful2.u();
}
x[i].g(); //這是動(dòng)態(tài)綁定,將方法的調(diào)用和方法主體關(guān)聯(lián)起來(lái)就是動(dòng)態(tài)綁定。
}
運(yùn)行結(jié)果:
g() in the Useful class // 由于x[0].g()這一句中調(diào)用g()方法實(shí)際的對(duì)象類(lèi)型是
// 基類(lèi)Useful類(lèi)型,故將調(diào)用基類(lèi)中的f()方法。(沒(méi)有轉(zhuǎn)型)
g() in the MoreUseful class // 由于x[1].g()這一句中調(diào)用g()方法實(shí)際的對(duì)象類(lèi)型是
//子類(lèi)Moreuseful類(lèi)型,故將調(diào)用子類(lèi)Moreuseful中
// 覆蓋父 類(lèi)的g()方法。(向上轉(zhuǎn)型)
u() in the MoreUseful2 class // 由于x[2]是MoreUseful2類(lèi)型,故可以對(duì)其向下轉(zhuǎn)型調(diào)
// 用MoreUseful2中的擴(kuò)展方法u()。(向下轉(zhuǎn)型)
g() in the MoreUseful2 class // 又x[2].g()這一句中調(diào)用g()方法實(shí)際的對(duì)象類(lèi)型是子
// 類(lèi)Moreuseful類(lèi)型,故將調(diào)用子類(lèi)Moreuseful中
// 覆蓋父類(lèi)的g()方法。(向上轉(zhuǎn)型)
u() in the ExtendsMoreUseful2 class // 由于x[3]是ExtendMoreUseful2類(lèi)型,
// 它是MoreUseful2的子類(lèi),存在is-a關(guān)系,故可以對(duì)其向下轉(zhuǎn)
// 型,將調(diào)ExtendMoreUseful2中的擴(kuò)展的方法u()。(向下轉(zhuǎn)型)
g() in the ExtendsMoreUseful2 class // 又x[3].g()這一句中調(diào)用g()方法實(shí)際的對(duì)象類(lèi)型
//是子類(lèi)ExtendMoreuseful類(lèi)型故將調(diào)用
// 子類(lèi)ExtendMoreuseful中覆蓋父類(lèi)的g()方法。(向上轉(zhuǎn)型)
分析和結(jié)論:
(一)向上轉(zhuǎn)型
(1)定義: 把對(duì)某個(gè)對(duì)象的引用視為對(duì)其基類(lèi)引用的做法被稱(chēng)為“向上轉(zhuǎn)型”。
這主要是由于子類(lèi)的對(duì)象可以看成是基類(lèi)的對(duì)象這原因而得來(lái)的,也就是具有is-a關(guān)系。
比如:
Useful useful = new MoreUseful();//右邊是一個(gè)子類(lèi)的對(duì)象,而左邊是一個(gè)父類(lèi)類(lèi)型
//的變量,指向右邊的子類(lèi)對(duì)象。
(2)基類(lèi)可以接收發(fā)給導(dǎo)出類(lèi)的任何消息,因?yàn)槎哂型耆嗤慕涌冢覀冎恍枰?span lang="EN-US">
從導(dǎo)出類(lèi)向上轉(zhuǎn)型,永遠(yuǎn)不需要知道正在處理的對(duì)象的確切類(lèi)型,這也就是多態(tài)性決
定的。利用多態(tài)性,具有同樣方法名和方法特征的方法根據(jù)調(diào)用方法的對(duì)象的類(lèi)型,
可以產(chǎn)生不同的動(dòng)作,這極大地增加了程序員的表達(dá)能力。
回頭再看一看上面這個(gè)例子中的一段for循環(huán)代碼,
for(int i=0;i<x.length;i++)
{
if(x[i] instanceof MoreUseful2) //判斷instanceof左邊的對(duì)象是否是右邊的類(lèi)的實(shí)例。
{
MoreUseful2 moreuseful2 = (MoreUseful2)x[i]; //向下轉(zhuǎn)型(具體解釋見(jiàn)下面的分析)
moreuseful2.u();
}
x[i].g(); //動(dòng)態(tài)綁定
}
主要看x[i].g();這一句話(huà),現(xiàn)在我們還不知道x[i]這個(gè)到底是指代哪一個(gè)Useful對(duì)象,
在這種情況下,編譯器是怎么知道調(diào)用哪個(gè)方法的呢?這是一個(gè)動(dòng)態(tài)綁定的問(wèn)題,見(jiàn)
下面。
(二)動(dòng)態(tài)綁定
(1)定義:將方法的調(diào)用和方法主體關(guān)聯(lián)起來(lái)就是動(dòng)態(tài)綁定。
比如:x[i].g();這就是一個(gè)動(dòng)態(tài)綁定,x[i]是一個(gè)對(duì)象類(lèi)型,g()是一個(gè)不知道是屬于
哪個(gè)對(duì)象的方法,將這兩個(gè)兩個(gè)聯(lián)合在一起,就是一個(gè)綁定。
(2)要注意:Java中除了static和final方法(private方法屬于final方法,因?yàn)?span lang="EN-US">fianl方法
不可以覆蓋,static方法是一個(gè)全局方法,屬于所有類(lèi)共享,不在多態(tài)范圍內(nèi))之外,
其他所有的方法都是動(dòng)態(tài)綁定,這就意味著在通常情況下,我們不必判定是否應(yīng)該進(jìn)
行動(dòng)態(tài)綁定,因?yàn)檫@個(gè)會(huì)自動(dòng)發(fā)生。
(3)接著上面的答復(fù)。編譯器是怎么知道調(diào)用哪個(gè)方法的呢?
主要還是x[i].g();這一句話(huà)的作用,x[i]在調(diào)用方法的時(shí)候,會(huì)調(diào)用“實(shí)際”的方法,這
個(gè)實(shí)際的方法由所引用的對(duì)象的類(lèi)型決定。那么調(diào)用的實(shí)際方法可能是父類(lèi)中沒(méi)有被
子類(lèi)覆蓋的方法,也可能是子類(lèi)中覆蓋父類(lèi)的方法,主要看調(diào)用這個(gè)方法的實(shí)際的對(duì)
象類(lèi)型是哪一個(gè)。
(三)向下轉(zhuǎn)型
既然有向上轉(zhuǎn)型,那么有沒(méi)有向下轉(zhuǎn)型呢?
答:有
(1)概述
繼承可以確保所有的子類(lèi)類(lèi)具有基類(lèi)的接口,且絕對(duì)不會(huì)少。那么子類(lèi)除了有父類(lèi)的
方法,也可以有自己的額外的新方法(這些方法是基類(lèi)所沒(méi)有的),那么一旦向上轉(zhuǎn)
型,就不能調(diào)用子類(lèi)中的新方法,那么能不能用一種方式調(diào)用這些新方法呢?當(dāng)然有
了,這時(shí)候就需要向下轉(zhuǎn)型。
(2)向下轉(zhuǎn)型
將超類(lèi)的引用強(qiáng)制轉(zhuǎn)換為子類(lèi)類(lèi)型就叫做向下轉(zhuǎn)型。
注意:將超類(lèi)的引用賦給為子類(lèi)類(lèi)型的變量(沒(méi)有進(jìn)行顯示地強(qiáng)制轉(zhuǎn)換)是一個(gè)編譯
錯(cuò)誤。
例子:
還是上面的for循環(huán)代碼
for(int i=0;i<x.length;i++)
{
if(x[i] instanceof MoreUseful2) // 判斷instanceof左邊的對(duì)象是否是右邊的類(lèi)的實(shí)例。
{
MoreUseful2 moreuseful2 = (MoreUseful2)x[i]; // 向下轉(zhuǎn)型
moreuseful2.u();
}
x[i].g();
}
分析:x[i]可以代表具體的Useful對(duì)象類(lèi)型,當(dāng)它是MoreUseful2或ExtendsMoreUseful2
對(duì)象類(lèi)型時(shí),就可以調(diào)用該對(duì)象的額外方法u(),v(),w(),也就是當(dāng)對(duì)象x[i]和Moreusful對(duì)
象存在is-a關(guān)系時(shí),才可以進(jìn)行向下轉(zhuǎn)型,如果要轉(zhuǎn)換的對(duì)象類(lèi)型與指定的對(duì)象類(lèi)型不
存在is-a關(guān)系時(shí),會(huì)產(chǎn)生一個(gè)ClassCastException異常。
總之:
向下轉(zhuǎn)型時(shí),對(duì)象只能強(qiáng)制轉(zhuǎn)換為其本身類(lèi)型或者其超類(lèi)類(lèi)型。比如,
當(dāng)x[i]ExtendsMoreUseful2對(duì)象時(shí),可以把他轉(zhuǎn)換為其本身ExtendsMoreUseful2對(duì)象類(lèi)
型,也可以把它轉(zhuǎn)換為其基類(lèi)MoreUseful2類(lèi)型。但是在編譯時(shí)候還不知道這個(gè)x[i]是代
表那個(gè)具體對(duì)象類(lèi)型只知道這個(gè)x[i]是基類(lèi)類(lèi)型引用,所以要用這樣的形式" (想要要得
到的類(lèi)型)x[i] " 進(jìn)行轉(zhuǎn)換。x[i]在這里是就我這個(gè)例子來(lái)說(shuō)明的,你也可以使用其它的
英文代替,其意義是一切符合規(guī)定的需要被轉(zhuǎn)換的對(duì)象。
下面還有個(gè)關(guān)于向上轉(zhuǎn)型和向下轉(zhuǎn)型的例子,
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();//向下轉(zhuǎn)型,可以調(diào)用導(dǎo)出類(lèi)中的擴(kuò)展方法
}
public static void playDemo2(ClassAbstract2 ObjectParameter)
{
ObjectParameter.play2();//向上轉(zhuǎn)型,可以調(diào)用導(dǎo)出類(lèi)中的覆蓋方法
}
/**
* @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);
}
}
運(yùn)行結(jié)果:
play1() is in the ClassDerived1
play2() is in the ClassDerived2