2013-02-01 46 views
3

假设我有这个接口和混凝土:我怎样才能让用Java泛型这项工作

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; 

我想改变报表创建实例作为如下:

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

reportBuilder.desiredMethod(); 

这样做我会得到“类型错配”。 我如何获得所需的功能?或者我怎么能改进我的课堂设计,使其更加灵活?

编辑:

通配符?解决了这个问题。但也引入了其他问题。 说我已经在这个接口方法:

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

到getStuff(...)的调用不会因为列表中包含通配符。一种修复方法是将ReportBuilder界面方法修改为:

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

但是后来我打破了具体的ReportBuilders。 (列表也是全球实例,其内容可能会有所不同)

任何想法?

回答

3

我想你应该申报报表生成器变量:

ReportBuilder<? extends MeterValue> reportBuilder; 

这使得reportBuilder有任何ReportBuilder类,与泛型类型的MeterValue,或实现接口的任何类。

顺便说一下,如果您不熟悉它,创建ReportBuilder实例似乎与factory method pattern类似,可能值得一读。 (当然也其他图案。)

+0

谢谢。我实施了工厂模式,有点。可以说具体的ReportBuilder是要创建的产品。那么工厂应该创建不同的产品。无论如何,我必须决定创建哪个工厂?那么为什么不直接创建产品。也许它会更灵活/可扩展的工厂模式的方式。 – Filip

+1

@Filip我认为这里有点混乱。抽象工厂和工厂方法模式是不同的!抽象工厂提供了一个由具体工厂实现的接口,其中包含创建实例的方法。工厂方法就是创建相应实例的独立方法 - 因此抽象工厂模式中的每个具体工厂实现都有一个工厂方法 - 创建实例的工厂方法。现在看来你只需要这种方法 - 所以只有一个工厂。所以你将有一个工厂创建生成器实例... – ppeterka

+0

好吧。我有一堂课; “BuilderFactory”具有返回“corect”构建器实例的静态工厂方法。 – Filip

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其中requeires至少MeterValueA的亚型。这不是类型安全的技术。在继承之后实例化具体类更适合于C++。在Java中,最好不要在继承后更改限制类型。

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

1)使用MeterValue单一接口。所有的差异都会转移到ReportBuilder继承。这意味着您的项目中具有并行继承。

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

+0

谢谢。通配符?解决了这个问题。但它也给了我其他问题:请参阅编辑。 – Filip

+0

好的,我明白了。所以我在调用getStuff()方法时必须有一个具体对象列表?你是否认为我应该将getStuff(...)中参数的签名更改为List <?扩展MeterValue>?在这种情况下,我必须在方法中键入元素,这是我从一开始就摆脱的意图。 – Filip

+0

在解决方案2)你不应该改变getStuff()的签名。您应该将具有通用参数的List传递给getStuff()。 – Taky

相关问题