具有类型安全实现的Java通用接口

时间:2010-09-12 19:44:07

标签: java generics interface

我正在寻找从通用框架调用特定接口的好方法。我将用代码举例说明。查看问题部分,主要包括示例代码以获得完整性,并将示例放入实际场景中。

实施例

假设我们想要根据组件列表构建报告。假设我们有两种特定的组件类型:

public interface Component { ... }
public class PDFComponents extends Component { ... }
public class WordComponents extends Component { ... }

每个组件都有一个ReportBuilder实现,例如,

public interface ReportBuilder { ... }
public class PDFReportBuilder extends ReportBuilder { ... }
public class WordReportBuilder extends ReportBuilder { ... }

构建特定的报告实现

public interface Report { ... }
public class PDFReport extends ReportBuilder { ... }
public class WordReport extends ReportBuilder { ... }

最后,我们提供了定位组件的服务,并从组件中生成报告。

public class ReportService {
    ReportComponentRepository repo;
    List<ReportBuilder> builders;

    public <T extends Report> T getReport(Class<T> reportType) {
        // Get report components. E.g., this might return List<PDFComponent>
        List<Component> reportComponents = repo.getReportComponents(id);

        // Build report from components using one of the registered builders
        for (ReportBuilder builder : builders) {
            if (builder.buildFor(reportType) {
                return builder.buildReport(report);
            }
        }
    }
}

使用服务的示例

List<PDFReport> reports = new ReportService().getReport(PDFReport.class);

问题

现在回答这个问题。我如何设计一个通用的ReportBuilder接口,它允许类型安全的实现?

,例如,选择界面:

public Report buildReport(List<? extends Component> components);

会导致其实施中的丑陋:

public class PDFReportBuilder implements ReportBuilder {

    @Override
    public Report buildReport(List<? extends Component> components) {
         PDFReport report;

         for (Component component : components) {
            if (component instanceOf PDFComponent) {
                // assemble report ... 
                report.includeComponent(component);
            }
        }

        return report;
    }
}

当我们真正希望PDFReportBuilder的界面为例如

 public Report buildReport(List<PDFComponent> component) { ... }

2 个答案:

答案 0 :(得分:2)

如果将Component的类型变为ReportBuilder的类型变量,则它可以工作:

public interface ReportBuilder<T extends Component> {
    public Report buildReport(List<T> components);
}

public class PDFReportBuilder implements ReportBuilder<PDFComponent> {
    public Report buildReport(List<PDFComponent> components);
}

您必须评估是否确实需要ReportBuilder中的类型变量。这并不总是正确的选择。此外,如果您还希望PDFReportBuilder.buildReport具有PDFReport的返回类型,那么您还需要将其作为类型变量(即public interface ReportBuilder<T extends Component, S extends Report>)。

答案 1 :(得分:2)

在我看来,就像你通过三个并行的继承层次结构来为一个混乱的实现做好准备。请问,为什么不能合并Component和ReportBuilder的共享行为?实际上,通过强制服务调用者知道他们想要的报告的子类,您将失去对组件的任何抽象。

我建议通过最小化或删除buildReport()

的参数来简化界面
 public class ReportService {
    ReportComponentRepository repo;
    List<ReportBuilder> builders;

    public <T extends Report> T getReport(Class<T> reportType) {

        // Build report from components using one of the registered builders
        for (ReportBuilder builder : builders) {
            if (builder.buildFor(reportType) {
                //don't pass components - if there's a requirement 
                //for a strongly typed subclass of Component, just 
                //let the Report instance figure it out.
                return builder.buildReport();
            }
        }
    }
}


//example use
public class PDFReportBuilder implements ReportBuilder {

    ComponentSource componentSource;

    @Override
    public Report buildReport() {
         PDFReport report;

         for (PDFComponent component : componentSource.getPDFComponents()) {
            // assemble report ... 
            report.includeComponent(component);
            // no instanceof operations!
        }

        return report;
    }
}