泛型类方法返回其自己的类型作为模板参数

时间:2012-05-29 21:55:21

标签: java generics inheritance

我正在设计一组在Document个对象中保存Folder个对象的泛型类:

// 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 个答案:

答案 0 :(得分: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​​是最好的了解方法,假设我们有一个名为Document的{​​{1}}实现:

SpreadSheet

然后你有一个名为class SpreadSheet implements Document<SpreadSheet> { @Override public Folder<SpreadSheet> getFolder() { return new SpreadSheetFolder(); } } 的{​​{1}}实现:

Folder

然后当您使用API​​时,您会这样做:

SpreadSheetFolder

它运作得很好。

答案 1 :(得分:2)

这似乎是满足您的需求的一种非常好的方式,不,没有更简单的方法来表达相同的内容(特别是,Java没有语法将类型参数约束为this的类型)。 / p>

但是,既然你扩展了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实现
List<D> findDocuments();

代替。