我如何使用java泛型来完成这项工作

时间:2013-02-01 09:09:25

标签: java generics class-design

假设我有这样的接口和混凝土:

public interface MeterValue {}
public class MeterValueA implements MeterValue {}
public class MeterValueB implements MeterValue {}

public interface ReportBuilder<T extends MeterValue> {} 
public class ReportBuilderA implements ReportBuilder<MeterValueA> {}
public class ReportBuilderB implements ReportBuilder<MeterValueB> {}

我有一个全球实例:

ReportBuilder<MeterValue> reportBuilder;

我想改变reportBuilder实例,如下所示:

if (isA())
   reportBuilder = new ReportBuilderA();
else
   reportBuilder = new ReportBuilderB();

reportBuilder.desiredMethod();

这样做我得到“Type Missmatch”。 我怎样才能获得所需的功能?或者我怎样才能提高课堂设计的灵活性?

修改

通配符?解决了这个问题。但也介绍了其他问题。 假设我在界面中有这些方法:

public interface ReportBuilder<T extends MeterValue> {
     public String getStuff(List<T> values);
} 

public class ReportBuilderA implements ReportBuilder<MeterValueA> {
     @Override
     public String getStuff(List<MeterValueA> values) { return "A"; }
}

public class ReportBuilderB implements ReportBuilder<MeterValueB> {
     @Override
     public String getStuff(List<MeterValueB> values) { return "B"; }
}

说我有这个例子:

  ReportBuilder<? extends MeterValue> reportBuilder = new ReportBuilderA();

  List<? extends MeterValue> list = new ArrayList<MeterValueA>();
  String s = reportBuilder.getStuff(list);

由于list包含通配符,因此对getStuff(...)的调用将不起作用。一个修复方法是将ReportBuilder接口方法修改为:

public String getStuff(List<? extends MeterValue> values);

然后我打破了具体的ReportBuilders。 (列表也是内容可能不同的全局实例)

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

我认为你应该将报告构建器变量声明为:

ReportBuilder<? extends MeterValue> reportBuilder;

这允许reportBuilder拥有任何ReportBuilder类,通用类型为MeterValue,或任何实现该接口的类。

顺便说一句,如果您已经不熟悉它,创建ReportBuilder实例似乎与factory method pattern类似,那么可能值得阅读它。 (当然还有其他模式。)

答案 1 :(得分:3)

public interface MeterValue {}

public class MeterValueA implements MeterValue {}

public class MeterValueB implements MeterValue {}

public interface ReportBuilder<T extends MeterValue> {
    void desiredMethod();
}

public class ReportBuilderA implements ReportBuilder<MeterValueA> {
    @Override
    public void desiredMethod() {}
}

public class ReportBuilderB implements ReportBuilder<MeterValueB> {
    @Override
    public void desiredMethod() {}
}

void f() {
    ReportBuilder<? extends MeterValue> reportBuilder = null;

    if (Math.random() > 0.5)
        reportBuilder = new ReportBuilderA();
    else
        reportBuilder = new ReportBuilderB();

    reportBuilder.desiredMethod();
}

其中:ReportBuilder<? extends MeterValue>表示通用参数可以扩展 MeterValue 接口。

请参阅tutorial以阅读有关通配符声明的更多说明。


注意:请在特定情况下查看此代码,以避免Parallel inheritance气味。如果要添加一些新的MeterValue,你应该添加ReportBuilder它应该意味着你可能有这个问题。


修改

您不能将具有MeterValue子类型的数组传递给ReportBuilderA,它至少会重新记录MeterValueA。它不是类型安全的技术。继承后实例化具体类更适合C ++。在Java中,最好不要在继承后更改受限制的类型。

可能的解决方案,可能适用于您的具体案例:

1)为MeterValue使用单个界面。所有差异都转移到ReportBuilder继承。这意味着您在项目中具有并行继承。

2)在调用List<MeterValueA>方法时使用具体的getStuff()。处理List<MeterValueA>List<MeterValueB>的所有共享逻辑都转移到带签名的方法:public void sharedLogic(List<? extends MeterValue> list)