具有类型安全实现的Java通用接口[英] Java generic interfaces with typesafe implementations

本文是小编为大家收集整理的关于具有类型安全实现的Java通用接口的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在寻找从通用框架中调用特定接口的好替代方案.我将用代码举例说明.查看问题部分,示例代码主要包括在内,以进行彻底,并将示例置于真实的情况下.

示例

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

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

每个组件都有一个报告启动器实现,例如,

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) { ... }

推荐答案

如果将组件类型放入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>).

其他推荐答案

在我看来,就像您通过三个并行继承层次结构来为凌乱的实现做好准备.请问,为什么您不能合并组件和报告企业的共享行为?有效地,您通过强迫服务呼叫者了解他们想要的报告的子类而失去了组件上的任何抽象.

我建议通过最大程度地减少或消除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;
    }
}

本文地址:https://www.itbaoku.cn/post/627337.html

问题描述

I'm look for good alternatives to invoking a specific interface from a generic framework. I'll exemplify with code. Look to the question part, the example code is primarily included for thoroughness, and for putting the example into a real scenario.

Example

Assume we want to build a report based on a list of components. Say we have two specific component types:

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

Each component has a ReportBuilder implementation, e.g.,

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

Which builds specific report implementations

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

Finally we have the service which locates components and produces a report out of the components.

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);
            }
        }
    }
}

Example using the service

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

Question

Now to the question. How can I design a generic ReportBuilder interface which allows typesafety to it's implementations?

E.g., choosing the interface:

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

would cause ugliness in it's implementations:

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;
    }
}

when we really desire the interface for PDFReportBuilder to be e.g.,

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

推荐答案

It works if you make the type of Component into a type variable for 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);
}

You'll have to evaluate whether you really want a type variable in ReportBuilder though. It's not always the correct choice. Additionally, if you also want PDFReportBuilder.buildReport to have a return type that is PDFReport, then you need to have that as a type variable as well (i.e., public interface ReportBuilder<T extends Component, S extends Report>).

其他推荐答案

It seems to me like you're setting yourself up for a messy implementation by having three parallel inheritance hierarchies. May I ask, why can't you merge the Component and ReportBuilder's shared behavior? Effectively, you lose any abstraction on the components by forcing the service caller to know the subclass of the Report they want.

I'd suggest simplifying the interface by minimizing or eliminating the parameters to 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;
    }
}