2017-03-06 182 views
0

的實例例如,我有Animal類,Cat類(Bird,Dog,Fish ...),它擴展了Animal。現在我想宣佈寵物類,這也是動物類,我希望它可以用任何現有的動物來構建,例如貓。是否可以擴展類

所以我需要構造是這樣的:

class Pet extends Animal{ 
    private String nickname; 
    private Animal kind; 

    Pet(Animal a, String nickname){ 
     <...>; 
     this.nickname = nickname; 
    } 
} 

編輯: 我想是這樣的:

Cat cat = new Cat(); 
Animal pet = new Pet(cat, "Foo"); 
if (pet instanceof Pet){ 
    if (pet.kind instanceof Cat){ 
     Pet.sayMeow(); 
    } 
} 

是不是意味着我只需要Animal()(這是受保護的)構造函數< ...>?

+3

問問自己:什麼是'寵物'應該通過'動物'做?你現在什麼都不做,這就是爲什麼這個問題不清楚(不清楚你傳遞'動物'的理由是什麼)。 – Tom

+0

@Tom說了些什麼:關於你想達到什麼的更多信息會很好。目前,這聽起來像你可以使用'interface'的東西。 – domsson

+2

如果你想說要採用現有的'Animal'實例並追溯**將該實例**轉換爲'Pet',則不能這樣做。你可以創建一個新的'Pet',它是從一個'Animal'實例初始化的,但它將是分開的。實例的類在創建時被鎖定。 (我們可以對[接口]有不同的視圖,有些代碼可能只知道它是'Animal',其他代碼可能知道它是'Cat',但*實例*在創建時具有特定的類集。 –

回答

1

即使在您編輯之後,您的意圖對我來說也不是很清楚。看起來好像你只是想找到一種方法,允許你在運行時說:「這隻貓以前是流浪的,但它現在只是一個人的寵物!」。

如果這是正確的,你可以去一個非常簡單和直接的解決方案 - 添加此功能從右到Animal類:

public class Animal { 

    private boolean isPet = false; 
    private String nickname = ""; 

    public Animal() { 
    /*...*/ 
    } 

    public makeStray() { 
    isPet = false; 
    nickname = ""; 
    } 

    public makePet(String nickname) { 
    isPet = true; 
    this.nickname = nickname; 
    } 

    public boolean isPet() { 
    return isPet; 
    } 

    public void makeNoise() { 
    /* Override in child classes */ 
    } 

} 

根據你的榜樣,然後你可以簡單地做:

Animal cat = new Cat(); 
cat.makePet("Foo"); 
if (cat.isPet()) { // Apparently, only pet cats ever meow. 
    cat.makeNoise(); // Cats will meow, dogs will bark, ... 
} 

但是,請注意,這種編碼方式可以快速膨脹一個類。這實際上取決於你打算如何處理它。我會說這是快速'n'髒解決方案。

對於更復雜的解決方案,請檢查其他答案。

EDIT1:正如Fildor正確指出的,有一個方法sayMeow()不是一個好主意。在Animal中最好有makeNoise()方法,並在子類中重寫它以獲取不同類型動物的特定行爲。現在,如果您從不想實際創建Animal類的實例,則還可以創建類abstract以及makeNoise()方法。這將確保每個孩子班級都必須實施makeNoise()方法。或者,如果方法沒有被覆蓋,或許你對靜默動物的默認行爲沒有問題。

編輯2This answer到相關的問題可能會對您的情況更清楚。它是關於C#的,但原理轉化爲Java。

+0

我重新編輯了我的編輯,現在它正確顯示我想要的內容 – Noqrax

+0

並非如此。你向我們展示了一些你想要工作的代碼,但不是它背後的* intent *。如果我正確讀了它,意圖是:「我想能夠判斷某隻動物是不是寵物,並根據它來採取行動」。這可以用上面顯示的代碼。 – domsson

0

你描述的是完全可能的,俗稱「裝飾模式」。

僅限鏈接的答案很糟糕,稍後我會詳細闡述時間。

同時,維基百科有更多信息:Decorator Pattern


1 Cat cat = new Cat(); 
2 Animal pet = new Pet(cat, "Foo"); 
3 if (pet instanceof Pet){ 
4 if (pet.kind instanceof Cat){ 
5   Pet.sayMeow(); 
6 } 
7 } 

這有你需要使用instanceOf的缺點。通常,你會讓你的Animal類有一個方法 - 我們稱之爲makeNoise。可能是抽象的。您動物實現(Cat,Dog ...)然後將覆蓋該方法,使其各自的噪音(「樹皮」,「喵」...)。

在片段中,似乎只有寵物可以發出噪音......這使得它有點複雜,因爲會有多種方式來做到這一點。

您可以讓裝飾者保存聲音並覆蓋makeNoise來發出聲音。像這樣:

Cat catInstance = new Cat(); 
catInstance.makeNoise(); // Default impl: NOP => "" - no sound. 
Animal pet = new Pet(catInstance, "Mieow"); 
pet.makeNoise(); // => "Mieow" 

這一切的一點是:你想避免使用instanceof。你不在乎動物是貓還是貓,寵物貓還是寵物狗。如果告訴他們,他們應該發出正確的聲音。所以你可以有一個「動物」的集合,告訴他們所有的「makeNoise」,每個人都將保持沉默,吠叫或mieow,而不必關心他們是否是寵物或Animal的特定孩子。

編輯:再次讀我的答案,它更像是一個政策(策略模式)比裝飾者。 該策略會改變如何完成某些操作,而裝飾器將添加該功能。

所以要成爲一個真正的裝飾者,這將意味着makeNoise將在Pet的界面。這意味着你無法在動物上調用該方法。

這就是爲什麼我改變我的建議從「裝飾」到「Strategy」模式。

上面的例子仍然成立。你會有一種「默認」的策略,並通過使用裝飾類實現方法來注入「寵物」策略。

當然,所有這些也可以用不同的方式來更嚴格地實現模式。

最後,if(x instanceof X) ...總是叮叮噹噹的「訪客」 - 也是。

+0

'makeNoise()'上的優點 - 以前完全忽略了。現在編輯我的答案。 – domsson