2017-03-03 73 views
0

好的,雖然我試圖找到解釋問題的標題,但我可能需要對其進行擴展。將超類傳遞給需要子類的方法

最近我實現了一個用來控制磁帶庫的小程序。知道它必須適用於多種不同類型的磁帶庫,因此開發了以下設計。

interface Tapelibrary<T extends TapeDrive> { 
    List<T> getListofDrives(); 
    void doSomethingWithDrive(T d); 
} 

class SpecificTapeLibrary implements Tapelibrary<HPDrive> { 
    private List<HPDrive> driveList; 

    SpecificTapeLibrary() { 
     driveList.add(new HPDrive()); 
     driveList.add(new HPDrive()); 
     driveList.add(new HPDrive()); 
    } 

    @Override 
    public List<HPDrive> getListofDrives() { 
     return driveList; 
    } 

    @Override 
    public void doSomethingWithDrive(HPDrive d) { 
     d.doSomethingHPspecific(); 
    } 
} 

abstract class TapeDrive { 
    void doSomething() { 
    } 
} 

class HPDrive extends TapeDrive { 
    void doSomethingHPspecific() { 
    } 
} 

正確磁帶庫通過基於命令行參數的工廠確定。

public static void main(String[] args) { 
    Tapelibrary<? extends TapeDrive> t = new TapeLibraryFabric().get(); 
    List<? extends TapeDrive> listOfDrives = t.getListofDrives(); 

    // the user selects a drive by using a small UI or something 
    TapeDrive selectedDrive = listOfDrives.get(0); 

    t.doSomethingWithDrive(selectedDrive); // compiler error 
} 

這確實在SpecificTapeLibrary

意義,因爲編譯器必須顯式轉換超TapeDrive的亞型HPDrive這是由doSomethingWithDrive(HPDrive)預期的方法這將如何在一個很好的得到解決OOP方式?我最終沒有在doSomethingWithDrive方法中使用泛型並進行投射(如下所示:How to Pass a Child Class into a method requiring Super Class as parameter)。但這不可能是最佳解決方案。

在寫這篇文章時,另一個解決方案彈出到我的頭,這是更清潔。 DriveSelector類封裝了選擇過程。

class DriveSelector { 
    <T> T selectDrive(List<T> inputList) { 
     // give the user an UI or something to select a drive 
     return inputList.get(0); 
    } 
} 

// the tape library then uses the selector 
public void doSomethingWithSelectedDrive(DriveSelector selector) { 
    HPDrive t = selector.selectDrive(driveList); 
    t.doSomethingHPspecific(); 
} 

還有其他想法嗎?

+0

爲什麼「類SpecificTapeLibrary實現了Tapelibrary 」需要HPDrive而不是TapeDrive? – efekctive

+0

你的'DriveSelector'類沒有用處:你可以直接調用'inputList.get(0)'得到完全相同的結果。 –

+0

@efekctive - 這個想法是,一個非抽象的(這是一個真實的世界磁帶庫)必須有一個非抽象的驅動器構建它。我可能會看到另一個缺陷,因爲這意味着磁帶庫總是有一種類型的驅動器構建到其中,但情況並非如此。 – user264235

回答

1

貴公司的所有工作,在一個通用的方法:

Tapelibrary<? extends TapeDrive> t = new TapeLibraryFabric().get(); 
doStuff(t); 

Ideone demo

其工作原理是,它消除一切:

static <T extends TapeDrive> void doStuff(Tapelibrary<T> t) { 
    List<T> listOfDrives = t.getListofDrives(); 

    // the user selects a drive by using a small UI or something 
    T selectedDrive = listOfDrives.get(0); 

    t.doSomethingWithDrive(selectedDrive); 
} 

然後從你的主要方法調用此的通配符 - 關於通配符的事情是編譯器將每個對象視爲不同的對象,即使這些值是從單個通用實例派生而來的。通過將這些東西放入這樣的通用方法中,您可以讓編譯器知道所有的T都是相同的類型 - 因此它可以知道這些調用是安全的。

+0

非常感謝您的回答。我想我明白了。 – user264235