2012-05-29 106 views
1

我設計了一套持有Folder對象中Document對象泛型類:泛型類方法返回了自己的類型作爲模板參數

// Folder, which holds zero or more documents 
public interface Folder<DocType extends Document> 
{ 
    // Locate matching documents within the folder 
    public ArrayList<DocType> findDocuments(...); 
    ... 
} 

// Document, contained within a folder 
public interface Document 
{ 
    // Retrieve the parent folder 
    public Folder getFolder(); // Not correct 
    ... 
} 

這些類,然後擴展到實際執行的文件夾和文件類型。問題在於Document.getFolder()方法需要返回類型爲Folder<DocType>的對象,其中DocTypeDocument的實際實現類型。這意味着該方法需要知道它自己的具體類類型是什麼。

所以我的問題是,如果Document類中聲明,而不是像這樣:

// Document, contained within a Folder 
public interface Document<DocType extends Document> 
{ 
    // Retrieve the parent folder 
    public Folder<DocType> getFolder(); 
    ... 
} 

或者是有一個更簡單的方法來做到這一點?上面的代碼需要具體的實現看起來像這樣:

public class MyFolder 
    implements Folder<MyDocument> 
{ ... } 

public class MyDocument 
    implements Document<MyDocument> 
{ ... } 

這是Document<MyDocument>部分似乎有點怪我。真的有必要嗎?

(道歉,如果這是一個重複。我找不到我的檔案尋找確切的答案)

附錄

上面的原代碼使用ArrayList<DocType>,但像一些海報已經指出的那樣,我會是最好返回List,如:

public List<DocType> findDocuments(...); 

(該方法對我的問題並不重要,我的實際API返回Iterator,所以我只是想出了第一件事來簡化問題。)

+2

注意:如果可能,請使用接口(List)而不是實現類型(ArrayList)作爲返回值。 – Puce

+0

有時候泛型可能有點痛苦。但在這種情況下,我不確定我想要使用它們。任何你爲什麼不高興,如果有文件夾只是返回一個文件清單,並把它留在那? – ianpojman

+2

「文件夾」可以只包含一種「文檔」嗎? (這是你目前的通用接口建議。) – Jeffrey

回答

2

這似乎是一個aedequate方式ADRESS您的要求,並沒有,也沒有表達同樣簡單的方式(尤其是Java有沒有語法約束一個類型參數的this類型)。

但是,既然擴展了Document和Folder,可能實際上你需要一個相互遞歸的類型綁定。我的意思是,在你的解決方案,表達

new MyFolder().findDocuments().get(0).getFolder() 

的類型是Folder<MyDocument>,不MyFolder的。如果它需要MyFolder中,仿製藥獲得醜:

//Folder, which holds zero or more documents 
interface Folder<D extends Document<D, F>, F extends Folder<D, F>> { 
    ArrayList<D> findDocuments(); 
} 

// Document, contained within a folder 
interface Document<D extends Document<D, F>, F extends Folder<D, F>> { 
    F getFolder(); 
} 

然後,您可以實現:

class MyFolder implements Folder<MyDocument, MyFolder> { 
    @Override 
    public ArrayList<MyDocument> findDocuments() { 
     return new ArrayList<>(Collections.singletonList(new MyDocument())); 
    } 
} 

class MyDocument implements Document<MyDocument, MyFolder> { 
    @Override 
    public MyFolder getFolder() { 
     return new MyFolder(); 
    } 
} 

現在,

MyFolder folder = new MyFolder().findDocuments().get(0).getFolder(); 

編譯了。

順便說一句,使用ArrayList在一個界面上是相當有限制的,因爲當我想用Collections.singletonList說明kludge。你也(無意中?)否認Collections.unmodifiableList和朋友的服務。因此,通常人們通過聲明

List<D> findDocuments(); 

來代替實施者的判斷。

+0

是的,我預計完整的類型安全解決方案相當複雜。我可以看到,爲Folder和Document類提供'DocType'和'FolderType'作爲模板參數可以讓事情變得更簡單。爲了讓'getFolder()'返回確切的'MyFolder'類型而走完全程是很複雜的,但我明白了Java爲什麼需要它。 幸運的是,生成的客戶端代碼非常簡單直接。 –

3

您擁有它的方式對我來說似乎沒問題,除了DocumentFolder接口中缺少的類型參數和使用ArrayList(實現)而不是List(接口)。

interface Folder<T extends Document<T>> 
{ 
    public List<T> findDocuments(); 

} 

interface Document<T extends Document<T>> 
{ 
    public Folder<T> getFolder(); 
} 

使用API​​是知道的最好方式,假設我們有一個名爲SpreadSheetDocument實現:

class SpreadSheet implements Document<SpreadSheet> { 

    @Override 
    public Folder<SpreadSheet> getFolder() { 
     return new SpreadSheetFolder(); 
    } 
} 

然後你有一個Folder實現所謂SpreadSheetFolder

class SpreadSheetFolder implements Folder<SpreadSheet>{ 

    @Override 
    public List<SpreadSheet> findDocuments() { 
     return asList(new SpreadSheet(), new SpreadSheet()); 
    } 
} 

然後當你使用你的API,你會這樣做:

Document<SpreadSheet> d = new SpreadSheet(); 
Folder<SpreadSheet> folder = d.getFolder(); 

它工作得很好。

+1

+1,但是一個小問題:你有'findDocuments()'在'Folder'接口中返回'ArrayList ',但'SpreadSheetFolder'類中的'List ',這將是一個編譯器,時間錯誤。 –

+0

@DanielPryden好抓!我從編輯本身開始玩它,很難發現你犯了什麼錯誤。謝謝,我已經糾正了答案。 –

+0

這可能是最簡單的解決方案。它有一個小缺點,即@meriton指出,'getFolder()'不會返回確切的客戶端'SpreadSheetFolder'類型,但只能鍵入'Folder '。雖然這可能沒問題。 –

相關問題