<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 多態 我們在處理類的層次結構時,通常把一個對象看成是它所屬的基類,而不是把它當成具體類。通過這種方式,我們可以編寫出不局限于特定類型的代碼。在上個“形狀”的例子中,“方法”(method)操縱的是通用“形狀”,而不關心它們是“圓”、“正方形”、“三角形”還是某種尚未定義的形狀。所有的形狀都可以被繪制、擦除和移動,因此“方法”向其中的任何代表“形狀”的對象發送消息都不必擔心對象如何處理信息。 這樣的代碼不會受添加的新類型影響,并且添加新類型是擴展面向對象程序以處理新情況的常用方法。 例如,你可以通過通用的“形狀”基類派生出新的“五角形”形狀的子類,而不需要修改通用"形狀"基類的方法。通過派生新的子類來擴展設計的這種能力是封裝變化的基本方法之一。 這種能力改善了我們的設計,且減少了軟件的維護代價。如果我們把派生的對象類型統一看成是它本身的基類(“圓”當作“形狀”,“自行車”當作“車”,“鸕鶿”當作“鳥”等等),編譯器(compiler)在編譯時期就無法準確地知道什么“形狀”被擦除,哪一種“車”在行駛,或者是哪種“鳥”在飛行。這就是關鍵所在:當程序接收這種消息時,程序員并不想知道哪段代碼會被執行。“繪圖”的方法可以平等地應用到每種可能的“形狀”上,形狀會依據自身的具體類型執行恰當的代碼。 如果不需要知道執行了哪部分代碼,那我們就能添加一個新的不同執行方式的子類而不需要更改調用它的方法。那么編譯器在不確定該執行哪部分代碼時是怎么做的呢?舉個例子,下圖的**BirdController**對象和通用**Bird**對象中,**BirdController**不知道**Bird**的確切類型卻還能一起工作。從**BirdController**的角度來看,這是很方便的,因為它不需要編寫特別的代碼來確定**Bird**對象的確切類型或行為。那么,在調用**move()**方法時是如何保證發生正確的行為(鵝走路、飛或游泳、企鵝走路或游泳)的呢? ![Bird-example](https://lingcoder.gitee.io/onjava8/images/1545839316314.png) 這個問題的答案,是面向對象程序設計的妙訣:在傳統意義上,編譯器不能進行函數調用。由非 OOP 編譯器產生的函數調用會引起所謂的**早期綁定**,這個術語你可能從未聽說過,不會想過其他的函數調用方式。這意味著編譯器生成對特定函數名的調用,該調用會被解析為將執行的代碼的絕對地址。 通過繼承,程序直到運行時才能確定代碼的地址,因此發送消息給對象時,還需要其他一些方案。為了解決這個問題,面向對象語言使用**后期綁定**的概念。當向對象發送信息時,被調用的代碼直到運行時才確定。編譯器確保方法存在,并對參數和返回值執行類型檢查,但是它不知道要執行的確切代碼。 為了執行后期綁定,Java 使用一個特殊的代碼位來代替絕對調用。這段代碼使用對象中存儲的信息來計算方法主體的地址(此過程在多態性章節中有詳細介紹)。因此,每個對象的行為根據特定代碼位的內容而不同。當你向對象發送消息時,對象知道該如何處理這條消息。在某些語言中,必須顯式地授予方法后期綁定屬性的靈活性。例如,C++ 使用**virtual**關鍵字。在這些語言中,默認情況下方法不是動態綁定的。在 Java 中,動態綁定是默認行為,不需要額外的關鍵字來實現多態性。 為了演示多態性,我們編寫了一段代碼,它忽略了類型的具體細節,只與基類對話。該代碼與具體類型信息分離,因此更易于編寫和理解。而且,如果通過繼承添加了一個新類型(例如,一個六邊形),那么代碼對于新類型的 Shape 就像對現有類型一樣有效。因此,該程序是可擴展的。 代碼示例: ~~~ void doSomething(Shape shape) { shape.erase(); // ... shape.draw(); } ~~~ 此方法與任何**Shape**對話,因此它與所繪制和擦除的對象的具體類型無關。如果程序的其他部分使用`doSomething()`方法: ~~~ Circle circle = new Circle(); Triangle triangle = new Triangle(); Line line = new Line(); doSomething(circle); doSomething(triangle); doSomething(line); ~~~ 可以看到無論傳入的“形狀”是什么,程序都正確的執行了。 ![shape-example](https://lingcoder.gitee.io/onjava8/images/1545841270997.png) 這是一個非常令人驚奇的編程技巧。分析下面這行代碼: ~~~ doSomething(circle); ~~~ 當預期接收**Shape**的方法被傳入了**Circle**,會發生什么。由于**Circle**也是一種**Shape**,所 以`doSomething(circle)`能正確地執行。也就是說,`doSomething()`能接收任意發送給**Shape**的消息。這是完全安全和合乎邏輯的事情。 這種把子類當成其基類來處理的過程叫做“向上轉型”(**upcasting**)。在面向對象的編程里,經常利用這種方法來給程序解耦。再看下面的`doSomething()`代碼示例: ~~~ shape.erase(); // ... shape.draw(); ~~~ 我們可以看到程序并未這樣表達:“如果你是一個 Circle ,就這樣做;如果你是一個 Square,就那樣做...”。若那樣編寫代碼,就需檢查 Shape 所有可能的類型,如圓、矩形等等。這顯然是非常麻煩的,而且每次添加了一種新的 Shape 類型后,都要相應地進行修改。在這里,我們只需說:“你是一種幾何形狀,我知道你能刪掉`erase()`和繪制`draw()`,你自己去做吧,注意細節。” 盡管我們沒作出任何特殊指示,程序的操作也是完全正確和恰當的。我們知道,為 Circle 調用`draw()`時執行的代碼與為一個 Square 或 Line 調用`draw()`時執行的代碼是不同的。但在將`draw()`信息發給一個匿名 Shape 時,根據 Shape 句柄當時連接的實際類型,會相應地采取正確的操作。這非常神奇,因為當 Java 編譯器為`doSomething()`編譯代碼時,它并不知道自己要操作的準確類型是什么。 盡管我們確實可以保證最終會為 Shape 調用`erase()`和`draw()`,但并不能確定特定的 Circle,Square 或者 Line 調用什么。最后,程序執行的操作卻依然是正確的,這是怎么做到的呢? 發送消息給對象時,如果程序不知道接收的具體類型是什么,但最終執行是正確的,這就是對象的“多態性”(Polymorphism)。面向對象的程序設計語言是通過“動態綁定”的方式來實現對象的多態性的。編譯器和運行時系統會負責對所有細節的控制;我們只需知道要做什么,以及如何利用多態性來更好地設計程序。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看