【问题标题】:How can i make this work with java generics我怎样才能使这个工作与java泛型
【发布时间】:2013-02-01 09:09:25
【问题描述】:

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

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

这样做我得到“类型不匹配”。 如何获得我想要的功能?或者我该如何改进我的课程设计以使其更加灵活?

编辑:

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

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。 (列表也是一个全局实例,内容可能会有所不同)

有什么想法吗?

【问题讨论】:

    标签: java generics class-design


    【解决方案1】:

    我认为您应该将报表生成器变量声明为:

    ReportBuilder<? extends MeterValue> reportBuilder;
    

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

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

    【讨论】:

    • 谢谢。我已经实现了工厂模式。可以说具体的 ReportBuilders 是要创建的产品。然后工厂应该创造不同的产品。无论如何,我必须决定创建哪个工厂?那为什么不直接创建产品。也许工厂模式的方式会更灵活/可扩展。
    • @Filip 我认为这里有些混乱。抽象工厂和工厂方法模式是不一样的!抽象工厂提供了一个由具体工厂实现的接口,其中包含创建实例的方法。工厂方法只是创建适当实例的单独方法 - 因此抽象工厂模式中的每个具体工厂实现都有一个工厂方法 - 创建实例的方法。现在看来你只需要这种方法——所以只有一个工厂。因此,您将拥有一个创建构建器实例的工厂...
    • 好的。我有课; “BuilderFactory”,它有一个返回“正确”构建器实例的静态工厂方法。
    • @Filip 我认为在你的情况下,这正是正确的方法。我很高兴发现有人关心编码很好 :) 继续保持良好、高质量的工作!
    【解决方案2】:
    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&lt;? extends MeterValue&gt; 表示泛型参数可以扩展MeterValue接口。

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


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


    编辑:

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

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

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

    2) 调用getStuff() 方法时使用具体的List&lt;MeterValueA&gt;。用于处理 List&lt;MeterValueA&gt;List&lt;MeterValueB&gt; 的所有共享逻辑移至带有签名的方法:public void sharedLogic(List&lt;? extends MeterValue&gt; list)

    【讨论】:

    • 谢谢。通配符?解决了这个问题。但它也给我带来了其他问题:见编辑。
    • 好的,我明白了。所以在调用 getStuff() 方法时我必须有一个具体对象的列表吗?您是否认为我应该将 getStuff(...) 中的参数签名更改为 List?在这种情况下,我必须对方法中的元素进行类型转换,这是我从一开始就打算摆脱的。
    • 在解决方案 2) 中,您不应更改 getStuff() 的签名。您应该将带有具体通用参数的 List 传递给 getStuff()。
    猜你喜欢
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-21
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多