溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解Java編程多態(tài)

發(fā)布時(shí)間:2021-10-08 09:10:01 來源:億速云 閱讀:132 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“如何理解Java編程多態(tài)”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

目錄
  • 一、向上轉(zhuǎn)型

  • 二、轉(zhuǎn)機(jī)

    • 1、綁定

    • 2、擴(kuò)展性

    • 3、缺陷

  • 三、構(gòu)造器與多態(tài)

    • 1、構(gòu)造器的調(diào)用順序

    • 2、構(gòu)造器內(nèi)部的多態(tài)方法的行為

  • 四、協(xié)變返回類型

    • 五、繼承進(jìn)行設(shè)計(jì)

      前言:

      封裝,是合并屬性和行為創(chuàng)建一種新的數(shù)據(jù)類型,繼承是建立數(shù)據(jù)類型之間的某種關(guān)系(is-a),而多態(tài)就是這種關(guān)系在實(shí)際場(chǎng)景的運(yùn)用。

      多態(tài)就是把做什么和怎么做分開了;其中,做什么是指調(diào)用的哪個(gè)方法[play 樂器],怎么做是指實(shí)現(xiàn)方案[使用A樂器 使用B樂器],''分開了''指兩件事不在同一時(shí)間確定。

      一、向上轉(zhuǎn)型

      對(duì)象既可以作為它本身的類型使用,也可以作為它的基類型使用,而這種把對(duì)某個(gè)對(duì)象的引用視為對(duì)其基類型的引用的做法就是向上轉(zhuǎn)型。

      example:

      public enum Note {
       // 演奏樂符
          MIDDLE_C, C_SHARP, B_FLAT;
      
      }
      public class Instrument {
      
       // 樂器基類
          public void play(Note n) {
              print("Instrument.play()");
          }
      
      }
      public class Wind extends Instrument{
      
       // Wind是一個(gè)具體的樂器
          // Redefine interface method:
          public void play(Note n) {
              System.out.println("Wind.play() " + n);
          }
      
      }
      public class Music {
      
       // 樂器進(jìn)行演奏
          public static void tune(Instrument i) {
              i.play(Note.MIDDLE_C);
          }
      
          public static void main(String[] args) {
              Wind flute = new Wind();
              tune(flute); // 向上轉(zhuǎn)型
          }
      }

      好處:

      在上面例子中,如果讓tune方法接受一個(gè)Wind引用作為自己的參數(shù),似乎看起來更為直觀,但是會(huì)引發(fā)一個(gè)問題:這個(gè)時(shí)候你就需要為系統(tǒng)中Instrument的每種類型都編寫一個(gè)新的tune方法。所以我們只寫一個(gè)簡(jiǎn)單的方法,僅僅接收基類作為參數(shù),而不是特殊的導(dǎo)出類,這么做情況不是變得更好嗎。

      example:

      class Stringed extends Instrument {
          public void play(Note n) {
              print("Stringed.play() " + n);
          }
      }
      
      
      class Brass extends Instrument {
          public void play(Note n) {
              print("Brass.play() " + n);
          }
      }
      
      
      public class Music2 {
          public static void tune(Wind i) {
              i.play(Note.MIDDLE_C);
          }
      
          public static void tune(Stringed i) {
              i.play(Note.MIDDLE_C);
          }
      
      
          public static void tune(Brass i) {
              i.play(Note.MIDDLE_C);
      
          }
          public static void main(String[] args) {
              Wind flute = new Wind();
              Stringed violin = new Stringed();
              Brass frenchHorn = new Brass();
              tune(flute); // No upcasting
              tune(violin);
              tune(frenchHorn);
          }
      }

      二、轉(zhuǎn)機(jī)

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

      在上面這個(gè)方法中,它接收一個(gè)Instrument引用,那么在這種情況下,編譯器怎么樣才能知道這個(gè)instrument引用指向的是Wind對(duì)象呢? ——通過后期綁定

      1、綁定

      將一個(gè)方法調(diào)用同一個(gè)方法主體關(guān)聯(lián)起來稱為綁定。

      在程序執(zhí)行前進(jìn)行綁定,就是前期綁定,比如C語(yǔ)言就只有一種方法調(diào)用,就是前期綁定。

      在運(yùn)行時(shí)根據(jù)對(duì)象的類型進(jìn)行綁定就是后期綁定,也叫做動(dòng)態(tài)綁定或者運(yùn)行時(shí)綁定。

      Java中除了static方法和final方法之外,其它所有方法都是后期綁定,這意味著通常情況下,我們不必判定是否應(yīng)該進(jìn)行后期綁定——它會(huì)自動(dòng)發(fā)生。

      2、擴(kuò)展性

      由于有多態(tài)機(jī)制,所以可根據(jù)自己的需要向系統(tǒng)里加入任意多的新類型,同時(shí)毋需更改 true()方法。在一個(gè)設(shè)計(jì)良好的 OOP 程序中,我們的大多數(shù)或者所有方法都會(huì)遵從 tune()的模型,而且只與基礎(chǔ)類接口通信。我們說這樣的程序具有“擴(kuò)展性”,因?yàn)榭梢詮耐ㄓ玫幕A(chǔ)類繼承新的數(shù)據(jù)類型,從而新添一些功能。如果是為了適應(yīng)新類的要求,那么對(duì)基礎(chǔ)類接口進(jìn)行操縱的方法根本不需要改變,
      對(duì)于樂器例子,假設(shè)我們?cè)诨A(chǔ)類里加入更多的方法[what/adjust],以及一系列新類[Woodwind/Brass],,

      例子:

      class Instrument {
          void play(Note n) { print("Instrument.play() " + n); }
          String what() { return "Instrument"; }
          void adjust() { print("Adjusting Instrument"); }
      }
      
      
      class Wind extends Instrument {
          void play(Note n) { print("Wind.play() " + n); }
          String what() { return "Wind"; }
          void adjust() { print("Adjusting Wind"); }
      }
      
      
      class Percussion extends Instrument {
          void play(Note n) { print("Percussion.play() " + n); }
          String what() { return "Percussion"; }
          void adjust() { print("Adjusting Percussion"); }
      }
      
      class Stringed extends Instrument {
          void play(Note n) { print("Stringed.play() " + n); }
          String what() { return "Stringed"; }
          void adjust() { print("Adjusting Stringed"); }
      }
      
      
      class Brass extends Wind {
          void play(Note n) { print("Brass.play() " + n); }
          void adjust() { print("Adjusting Brass"); }
      }
      
      
      class Woodwind extends Wind {
          void play(Note n) { print("Woodwind.play() " + n); }
          String what() { return "Woodwind"; }
      }
      
      
      public class Music3 {
          // Doesn't care about type, so new types
          // added to the system still work right:
          public static void tune(Instrument i) {
              // ...
              i.play(Note.MIDDLE_C);
          }
      
          public static void tuneAll(Instrument[] e) {
              for(Instrument i : e)
                  tune(i);
          }
          public static void main(String[] args) {
              // Upcasting during addition to the array:
              Instrument[] orchestra = {
                  new Wind(),
                  new Percussion(),
                  new Stringed(),
                  new Brass(),
                  new Woodwind()
              };
              tuneAll(orchestra);
          }
      }

      為樂器系統(tǒng)添加更多的類型,而不用改動(dòng)tune方法。tune方法完全可以忽略它周圍代碼所發(fā)生的全部變化,依舊正常運(yùn)行。

      3、缺陷

      私有方法

      private方法被自動(dòng)修飾為final,而且對(duì)導(dǎo)出類是屏蔽的,所以在子類Derived類中的f方法是一個(gè)全新的方法。既然基類中的f方法在在子類Derived中不可見,那么也不能被重載。

      域與靜態(tài)方法

      任何域(field)的訪問操作都是由編譯器解析的,因此不是多態(tài)的。

      如果某個(gè)方法是靜態(tài)的,那么它就不具有多態(tài)性

      三、構(gòu)造器與多態(tài)

      通常,構(gòu)造器不同于其它方法,涉及到多態(tài)時(shí)也是如此。構(gòu)造器是不具有多態(tài)性的

      1、構(gòu)造器的調(diào)用順序

      基類的構(gòu)造器總是在導(dǎo)出類的構(gòu)造過程中被調(diào)用,而且按照繼承層次逐漸向上鏈接。使得每個(gè)基類的構(gòu)造器都能得到調(diào)用。

      2、構(gòu)造器內(nèi)部的多態(tài)方法的行為

      構(gòu)造器調(diào)用的層次結(jié)構(gòu)帶來一個(gè)問題:如果在一個(gè)構(gòu)造器內(nèi)部調(diào)用正在構(gòu)造的對(duì)象的某個(gè)動(dòng)態(tài)綁定方法,會(huì)發(fā)生什么?

      public class Glyph {
      
          void draw() { print("Glyph.draw()"); }
          Glyph() {
              print("Glyph() before draw()");
              draw(); // 調(diào)用正在構(gòu)造的對(duì)象的某個(gè)動(dòng)態(tài)綁定方法,對(duì)象的字段radius被初始化為0
              print("Glyph() after draw()");
          }
      
      }
      
      
      public class RoundGlyph extends Glyph{
      
          private int radius = 1;
      
          RoundGlyph(int r) {
              radius = r;
              print("RoundGlyph.RoundGlyph(), radius = " + radius);
          }
          void draw() {
              print("RoundGlyph.draw(), radius = " + radius);
          }
      
      
      }
      
      
      public class PolyConstructors {
          
          public static void main(String[] args) {
              new RoundGlyph(5);
          }
          
      }
      
      /* Output:
          Glyph() before draw()
          RoundGlyph.draw(), radius = 0
          Glyph() after draw()
          RoundGlyph.RoundGlyph(), radius = 5
      *///:~

      Glyph的構(gòu)造器中,我們調(diào)用了draw方法,因?yàn)檫@個(gè)是動(dòng)態(tài)綁定方法的緣故,我們就會(huì)調(diào)用導(dǎo)出類RoundGlyph中的draw方法,但是這個(gè)方法操縱的成員radius還沒初始化,所以就體現(xiàn)出問題了,結(jié)果中第一次輸出radius為0。

      所以初始化的實(shí)際過程是:

      • 1 在其他任何事物之前,將分配給對(duì)象的存儲(chǔ)空間初始化成二進(jìn)制的零

      • 2 如前所述調(diào)用基類構(gòu)造器

      • 3 按照聲明的順序調(diào)用成員的初始化方法

      • 4 調(diào)用導(dǎo)出類的構(gòu)造器主體

      四、協(xié)變返回類型

      在面向?qū)ο蟪绦蛟O(shè)計(jì)中,協(xié)變返回類型指的是子類中的成員函數(shù)的返回值類型不必嚴(yán)格等同于父類中被重寫的成員函數(shù)的返回值類型,而可以是更 "狹窄" 的類型。

      Java 5.0添加了對(duì)協(xié)變返回類型的支持,即子類覆蓋(即重寫)基類方法時(shí),返回的類型可以是基類方法返回類型的子類。協(xié)變返回類型允許返回更為具體的類型。

      例子:

      import java.io.ByteArrayInputStream;
      import java.io.InputStream;
      
      class Base
      {
          //子類Derive將重寫此方法,將返回類型設(shè)置為InputStream的子類
         public InputStream getInput()
         {
            return System.in;
         }
      }
      public  class Derive extends Base
      {
      
          @Override
          public ByteArrayInputStream getInput()
          {
      
              return new ByteArrayInputStream(new byte[1024]);
          }
          public static void main(String[] args)
          {
              Derive d=new Derive();
              System.out.println(d.getInput().getClass());
          }
      }
      /*程序輸出:
      class java.io.ByteArrayInputStream
      */

      五、繼承進(jìn)行設(shè)計(jì)

      class Actor {
       public void act() {
       }
      }
       
      class HappyActor extends Actor {
       public void act() {
        System.out.println("HappyActor");
       }
      }
       
      class SadActor extends Actor {
       public void act() {
        System.out.println("SadActor");
       }
      }
       
      class Stage {
       private Actor actor = new HappyActor();
       
       public void change() {
        actor = new SadActor();
       }
       
       public void performPlay() {
        actor.act();
       }
      }
       
      public class Transmogrify {
       public static void main(String[] args) {
        Stage stage = new Stage();
        stage.performPlay();
        stage.change();
        stage.performPlay();
       }
       
      }

      輸出:

        HappyActor
          SadActor

      一條通用的準(zhǔn)則是:“用繼承表達(dá)行為間的差異,并用字段表達(dá)狀態(tài)上的變化”。在上述例子中,兩者都用到了:通過繼承得到了兩個(gè)不同的類,用于表達(dá) act()方法的差異:而 Stage通過運(yùn)用組合使自己的狀態(tài)發(fā)生了變化。在這種情況下,這種狀態(tài)的改變也就產(chǎn)生了行為的改變。

      “如何理解Java編程多態(tài)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

      向AI問一下細(xì)節(jié)

      免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

      AI