<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    JAVA—咖啡館

    ——?dú)g迎訪問rogerfan的博客,常來《JAVA——咖啡館》坐坐,喝杯濃香的咖啡,彼此探討一下JAVA技術(shù),交流工作經(jīng)驗(yàn),分享JAVA帶來的快樂!本網(wǎng)站部分轉(zhuǎn)載文章,如果有版權(quán)問題請與我聯(lián)系。

    BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
      447 Posts :: 145 Stories :: 368 Comments :: 0 Trackbacks
    【IT168 技術(shù)文檔】“對于面向?qū)ο蟮某绦蛟O(shè)計(jì)語言,多型性是第三種最基本的特征(前兩種是數(shù)據(jù)抽象和繼承。”

      “多形性”(Polymorphism)從另一個(gè)角度將接口從具體的實(shí)施細(xì)節(jié)中分離出來,亦即實(shí)現(xiàn)了“是什么”與“怎樣做”兩個(gè)模塊的分離。利用多形性的概念,代碼的組織以及可讀性均能獲得改善。此外,還能創(chuàng)建“易于擴(kuò)展”的程序。無論在項(xiàng)目的創(chuàng)建過程中,還是在需要加入新特性的時(shí)候,它們都可以方便地“成長”。

      通過合并各種特征與行為,封裝技術(shù)可創(chuàng)建出新的數(shù)據(jù)類型。通過對具體實(shí)施細(xì)節(jié)的隱藏,可將接口與實(shí)施細(xì)節(jié)分離,使所有細(xì)節(jié)成為“private”(私有)。這種組織方式使那些有程序化編程背景人感覺頗為舒適。但多形性卻涉及對“類型”的分解。通過上一章的學(xué)習(xí),大家已知道通過繼承可將一個(gè)對象當(dāng)作它自己的類型或者它自己的基礎(chǔ)類型對待。這種能力是十分重要的,因?yàn)槎鄠€(gè)類型(從相同的基礎(chǔ)類型中衍生出來)可被當(dāng)作同一種類型對待。而且只需一段代碼,即可對所有不同的類型進(jìn)行同樣的處理。利用具有多形性的方法調(diào)用,一種類型可將自己與另一種相似的類型區(qū)分開,只要它們都是從相同的基礎(chǔ)類型中衍生出來的。這種區(qū)分是通過各種方法在行為上的差異實(shí)現(xiàn)的,可通過基礎(chǔ)類實(shí)現(xiàn)對那些方法的調(diào)用。

      在這一章中,大家要由淺入深地學(xué)習(xí)有關(guān)多形性的問題(也叫作動(dòng)態(tài)綁定、推遲綁定或者運(yùn)行期綁定)。同時(shí)舉一些簡單的例子,其中所有無關(guān)的部分都已剝除,只保留與多形性有關(guān)的代碼。

      7.1 上溯造型

      在第6章,大家已知道可將一個(gè)對象作為它自己的類型使用,或者作為它的基礎(chǔ)類型的一個(gè)對象使用。取得一個(gè)對象句柄,并將其作為基礎(chǔ)類型句柄使用的行為就叫作“上溯造型”——因?yàn)槔^承樹的畫法是基礎(chǔ)類位于最上方。

      但這樣做也會遇到一個(gè)問題,如下例所示(若執(zhí)行這個(gè)程序遇到麻煩,請參考第3章的3.1.2小節(jié)“賦值”):

      //: Music.java   // Inheritance & upcasting   package c07;   class Note {   private int value;   private Note(int val) { value = val; }   public static final Note   middleC = new Note(0),   cSharp = new Note(1),   cFlat = new Note(2);   } // Etc.   class Instrument {   public void play(Note n) {   System.out.println("Instrument.play()");   }   }   // Wind objects are instruments   // because they have the same interface:   class Wind extends Instrument {   // Redefine interface method:   public void play(Note n) {   System.out.println("Wind.play()");   }   }   public class Music {   public static void tune(Instrument i) {   // ...   i.play(Note.middleC);   }   public static void main(String[] args) {   Wind flute = new Wind();   tune(flute); // Upcasting   }   } ///:~

      其中,方法Music.tune()接收一個(gè)Instrument句柄,同時(shí)也接收從Instrument衍生出來的所有東西。當(dāng)一個(gè)Wind句柄傳遞給tune()的時(shí)候,就會出現(xiàn)這種情況。此時(shí)沒有造型的必要。這樣做是可以接受的;Instrument里的接口必須存在于Wind中,因?yàn)閃ind是從Instrument里繼承得到的。從Wind向Instrument的上溯造型可能“縮小”那個(gè)接口,但不可能把它變得比Instrument的完整接口還要小。

      7.1.1 為什么要上溯造型

      這個(gè)程序看起來也許顯得有些奇怪。為什么所有人都應(yīng)該有意忘記一個(gè)對象的類型呢?進(jìn)行上溯造型時(shí),就可能產(chǎn)生這方面的疑惑。而且如果讓tune()簡單地取得一個(gè)Wind句柄,將其作為自己的自變量使用,似乎會更加簡單、直觀得多。但要注意:假如那樣做,就需為系統(tǒng)內(nèi)Instrument的每種類型寫一個(gè)全新的tune()。假設(shè)按照前面的推論,加入Stringed(弦樂)和Brass(銅管)這兩種Instrument(樂器):

      //: Music2.java   // Overloading instead of upcasting   class Note2 {   private int value;   private Note2(int val) { value = val; }   public static final Note2   middleC = new Note2(0),   cSharp = new Note2(1),   cFlat = new Note2(2);   } // Etc.   class Instrument2 {   public void play(Note2 n) {   System.out.println("Instrument2.play()");   }   }   class Wind2 extends Instrument2 {   public void play(Note2 n) {   System.out.println("Wind2.play()");   }   }   class Stringed2 extends Instrument2 {   public void play(Note2 n) {   System.out.println("Stringed2.play()");   }   }   class Brass2 extends Instrument2 {   public void play(Note2 n) {   System.out.println("Brass2.play()");   }   }   public class Music2 {   public static void tune(Wind2 i) {   i.play(Note2.middleC);   }   public static void tune(Stringed2 i) {   i.play(Note2.middleC);   }   public static void tune(Brass2 i) {   i.play(Note2.middleC);   }   public static void main(String[] args) {   Wind2 flute = new Wind2();   Stringed2 violin = new Stringed2();   Brass2 frenchHorn = new Brass2();   tune(flute); // No upcasting   tune(violin);   tune(frenchHorn);   }   } ///:~

      這樣做當(dāng)然行得通,但卻存在一個(gè)極大的弊端:必須為每種新增的Instrument2類編寫與類緊密相關(guān)的方法。這意味著第一次就要求多得多的編程量。以后,假如想添加一個(gè)象tune()那樣的新方法或者為Instrument添加一個(gè)新類型,仍然需要進(jìn)行大量編碼工作。此外,即使忘記對自己的某個(gè)方法進(jìn)行過載設(shè)置,編譯器也不會提示任何錯(cuò)誤。這樣一來,類型的整個(gè)操作過程就顯得極難管理,有失控的危險(xiǎn)。

      但假如只寫一個(gè)方法,將基礎(chǔ)類作為自變量或參數(shù)使用,而不是使用那些特定的衍生類,豈不是會簡單得多?也就是說,如果我們能不顧衍生類,只讓自己的代碼與基礎(chǔ)類打交道,那么省下的工作量將是難以估計(jì)的。

      這正是“多形性”大顯身手的地方。然而,大多數(shù)程序員(特別是有程序化編程背景的)對于多形性的工作原理仍然顯得有些生疏。

      7.2 深入理解

      對于Music.java的困難性,可通過運(yùn)行程序加以體會。輸出是Wind.play()。這當(dāng)然是我們希望的輸出,但它看起來似乎并不愿按我們的希望行事。請觀察一下tune()方法:

      public static void tune(Instrument i) {   // ...   i.play(Note.middleC);   }

      它接收Instrument句柄。所以在這種情況下,編譯器怎樣才能知道Instrument句柄指向的是一個(gè)Wind,而不是一個(gè)Brass或Stringed呢?編譯器無從得知。為了深入了理解這個(gè)問題,我們有必要探討一下“綁定”這個(gè)主題。

      7.2.1 方法調(diào)用的綁定

      將一個(gè)方法調(diào)用同一個(gè)方法主體連接到一起就稱為“綁定”(Binding)。若在程序運(yùn)行以前執(zhí)行綁定(由編譯器和鏈接程序,如果有的話),就叫作“早期綁定”。大家以前或許從未聽說過這個(gè)術(shù)語,因?yàn)樗谌魏纬绦蚧Z言里都是不可能的。C編譯器只有一種方法調(diào)用,那就是“早期綁定”。

      上述程序最令人迷惑不解的地方全與早期綁定有關(guān),因?yàn)樵谥挥幸粋€(gè)Instrument句柄的前提下,編譯器不知道具體該調(diào)用哪個(gè)方法。

      解決的方法就是“后期綁定”,它意味著綁定在運(yùn)行期間進(jìn)行,以對象的類型為基礎(chǔ)。后期綁定也叫作“動(dòng)態(tài)綁定”或“運(yùn)行期綁定”。若一種語言實(shí)現(xiàn)了后期綁定,同時(shí)必須提供一些機(jī)制,可在運(yùn)行期間判斷對象的類型,并分別調(diào)用適當(dāng)?shù)姆椒āR簿褪钦f,編譯器此時(shí)依然不知道對象的類型,但方法調(diào)用機(jī)制能自己去調(diào)查,找到正確的方法主體。不同的語言對后期綁定的實(shí)現(xiàn)方法是有所區(qū)別的。但我們至少可以這樣認(rèn)為:它們都要在對象中安插某些特殊類型的信息。

      Java中綁定的所有方法都采用后期綁定技術(shù),除非一個(gè)方法已被聲明成final。這意味著我們通常不必決定是否應(yīng)進(jìn)行后期綁定——它是自動(dòng)發(fā)生的。

      為什么要把一個(gè)方法聲明成final呢?正如上一章指出的那樣,它能防止其他人覆蓋那個(gè)方法。但也許更重要的一點(diǎn)是,它可有效地“關(guān)閉”動(dòng)態(tài)綁定,或者告訴編譯器不需要進(jìn)行動(dòng)態(tài)綁定。這樣一來,編譯器就可為final方法調(diào)用生成效率更高的代碼。

      7.2.2 產(chǎn)生正確的行為

      知道Java里綁定的所有方法都通過后期綁定具有多形性以后,就可以相應(yīng)地編寫自己的代碼,令其與基礎(chǔ)類溝通。此時(shí),所有的衍生類都保證能用相同的代碼正常地工作。或者換用另一種方法,我們可以“將一條消息發(fā)給一個(gè)對象,讓對象自行判斷要做什么事情。”

      在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,有一個(gè)經(jīng)典的“形狀”例子。由于它很容易用可視化的形式表現(xiàn)出來,所以經(jīng)常都用它說明問題。但很不幸的是,它可能誤導(dǎo)初學(xué)者認(rèn)為OOP只是為圖形化編程設(shè)計(jì)的,這種認(rèn)識當(dāng)然是錯(cuò)誤的。

    posted on 2007-12-27 15:00 rogerfan 閱讀(331) 評論(0)  編輯  收藏 所屬分類: 【Java知識】
    主站蜘蛛池模板: 亚洲www在线观看| 国产免费久久精品久久久| 国产精品免费观看调教网| 国产精品hd免费观看| 一级做a毛片免费视频| 无遮挡国产高潮视频免费观看 | 国产成人无码免费视频97| 免费羞羞视频网站| 免费高清av一区二区三区| 免费无码黄网站在线观看| 日产乱码一卡二卡三免费| 四虎影永久在线高清免费| 亚洲不卡无码av中文字幕| 亚洲五月午夜免费在线视频| 国产精品亚洲高清一区二区| 亚洲伊人久久大香线蕉综合图片 | 在线观看H网址免费入口| 久久精品免费一区二区喷潮| 成人免费男女视频网站慢动作| 日本免费无遮挡吸乳视频电影| 亚洲国产精品国产自在在线| 精品亚洲综合在线第一区| 亚洲精品视频观看| 亚洲综合激情五月丁香六月| 久久综合亚洲色hezyo| fc2免费人成在线| 一级毛片免费视频| 久久精品网站免费观看 | 国产92成人精品视频免费| 最近免费中文字幕视频高清在线看| 日韩精品免费电影| 亚洲综合另类小说色区色噜噜| 亚洲国产成人一区二区精品区 | 亚洲中文字幕丝袜制服一区| 亚洲av无码国产精品色午夜字幕| 亚洲黑人嫩小videos| 亚洲aⅴ天堂av天堂无码麻豆| 久久久久免费视频| 波多野结衣在线免费观看| 国产又大又黑又粗免费视频| 亚洲乱码中文字幕久久孕妇黑人|