2014-02-11 76 views
2

給定以下代碼:爲什麼使用你的超類創建一個對象?

public class Musician { 

    public void play() { 
     // do something 
    } 
} 

public class Drummer extends Musician { 

    public void turnsDrumStick() { 
     // do something 
    } 
} 

public class Guitarist extends Musician { 

    public void strummingStrings() { 
     // do something 
    } 
} 

我可以使用多態做到以下幾點:

Musician m1 = new Guitarist(); 
    Musician m2 = new Drummer(); 

    m1 = m2; 

不過,我看不到子類的方法:

m1.strummingStrings(); //COMPILATION ERROR! 

如果我使用:

Guitarist m1 = new Guitarist(); 

難道不是更好嗎?使用Musico類型引用子類的對象的優點是什麼?例如,我可以將m1 = m2;歸爲一個可能性?還是還有其他優勢?

我看到了這個帖子,但我仍然不解:Using superclass to initialise a subclass object java

+0

另一個優點是,如果你想把所有的音樂家放到一個數據結構中(比如列表),你可以做到這一點,沒有你不能做的超類。 – BackSlash

+0

相關http://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface。不知道我是否稱它爲重複或不是。 –

+2

問題的標題不正確。它應該是類似於*「爲什麼使用超類參考的對象」* – UmNyobe

回答

0

如果你在寫一些實際需要和使用Musician的方法,你不小心要依賴於子類的功能。

在您的例子:

m1.strummingStrings(); //COMPILATION ERROR! 

那如果,比如說,你正在編寫一個接受Musician類測試驅動程序是一件好事。

當然,在許多方面,函數的開始和函數的結束之間,編碼標準並不重要,因爲讀者和維護者有可能全面瞭解正在發生的事情。因此,在例如:

void foo() { 
    Guitarist g; 
    g.play(); 
} 

void foo() { 
    Musician m; 
    m.play(); 
} 

很少會造成很大的差異。

Musician foo() { 
// ... 
} 

Guitarist foo() { 
// ... 
} 

會帶來很大的區別。在前一種情況下,客戶端代碼從未被允許期望吉他手,而在後面的代碼中,實現者永遠與永遠返回的吉他手相連,即使在將來會發現Banjoist可以更容易地實現並返回,因爲客戶端代碼依賴於它。

5

多態性的優勢是,當你可以撥打任何Musicianplay(),無論它是一個Guitarist,一個Drummer,或者你可能還沒有創建的Musician任何其他子類。

Musician m1 = new Guitarist(); 
Musician m2 = new Drummer(); 
m1.play(); 
m2.play(); 

這可能輸出類似

Guitarist strumming 
Drummer drumming 

如果替換你都子的play()方法。通過這種方式,調用play()並不需要知道其中Musician實現它確實是,只是它的一個Musician,它的保證有play()方法的代碼。

從超類引用中調用子類方法(如strummingStrings)不是多態的優點,因爲該方法只存在於子類中。它不保證存在於超類中。如果您需要調用一個只子類的方法,如strummingString,那麼你需要一個子類參考。

可能在超類Musician定義strummingStrings,多態性將工作,但這將是一個糟糕的設計。並不是所有的音樂家都可以在吉他上彈奏琴絃。

0

GuitaristDrummer被稱爲specializationsMusician。他們執行特定的技巧,他們有特定的玩法。如果你有一個Band,你可能有這樣的事情:

public class Band { 
    private List<Musician> musicians = new ArrayList<Musician>(); 
    public void addMusician(Musician m) { 
     musicians.add(m); 
    } 
    public void play() { 
     for (Musician m : musicians) { 
      m.play(); 
     } 
    } 
} 

現在,你需要重寫的DrummerplayMusician給那麼特殊行爲:

public class Drummer extends Musician { 
    @Override 
    public void play() { 
     this.turnsDrumStick(); 
    } 
    [...] 
} 
1

好問題,但是你遇到的問題是你的設計所固有的。

如果你做這樣的事情:

public class Musician { 

    public void play() { 
     // do something 
    } 
} 
. 

public class Drummer extends Musician { 

    public void turnsDrumStick() { 
     // do something 
    } 

    public void play() { 
     //...perhaps some other things.... 
     turnsDrumStick(); 
    } 
} 
. 

public class Guitarist extends Musician { 

    public void strummingStrings() { 
     // do something 
    } 

    public void play() { 
     strummingStrings(); 
     //other things 
    } 
} 

所以「帶」類由Musician秒的集合。樂隊課程通過調用播放方法告訴他們何時開始播放。實現細節留給亞型,這就是繼承的力量實現。

0

他們的方式,你寫你的類,真的沒有理由子類音樂家,因爲這兩個子類實際上都使用音樂家play()方法。將m1和m2聲明爲音樂家而不是他們各自的子類的好處是實例化類不需要知道play()方法是如何工作的;它可以叫m1.play()和m2.play()和子類知道該怎麼做。

從該類型的靈活性中受益,你需要調整你的對象。至少我會建議音樂家抽象,然後將子類中的play()方法改寫爲strum或drum。

如果您想進一步瞭解一下,您可以定義一個Play接口併爲類StrumGuitar和Drum實現該接口,從而封裝這些行爲。然後,例如,吉他手的play()方法可以將其行爲委託給StrumGuitar類。此外,實體化代碼就只需要調用play(),而不必擔心它做什麼。

這些技術有助於保持代碼的邏輯組織性和靈活性,並允許在運行時對實例化類未知子類的類型,這對您的代碼的靈活性會帶來巨大好處。

相關問題