Java泛型与工厂

时间:2013-11-20 13:18:41

标签: java generics factory

设定:

我有一些格式化程序的界面:

interface Formatter<T extends AbstractItem> {
  String format(T item);
}

我有一家工厂创建这样的格式化程序:

public class Factory {
  public static Formatter<? extends AbstractItem> create() {
    switch (something) {
      case SOMETHING: return new Formatter<SomeItem>() { String format(SomeItem item) {...}};
      case SOMETHING_ELSE: return new Formatter<OtherItem>() { String format(OtherItem item){...}};
    }

现在我用这个工厂来获取格式化程序&amp;我用它了:

1: Formatter formatter = Factory.create();
2: for (AbstractItem item : items) {
3:   formatter.format(item);
4: }

items列表仅包含AbstractItem能够处理的formatter个子类型。

问题:

我收到两个警告:

Line 1: Formatter is a raw type. References to generic type Formatter<T> should be parameterized.
Line 3: Type safety: The method format(AbstractItem) belongs to the raw type Formatter. References to generic type Formatter<T> should be parameterized.

好的,所以我尝试修复第一个:我知道工厂返回AbstractItem的后代:

1: Formatter<? extends AbstractItem> formatter = Factory.create();

现在第1行的警告消失,但第3行出现新错误:

Line 3: The method format(capture#3-of ? extends AbstractItem) in the type Formatter<capture#3-of ? extends AbstractItem> is not applicable for the arguments (AbstractItem).

因此,如果我理解正确,则抱怨AbstractItem不是AbstractItem的子类型(根据类型约束<? extends AbstractItem>的要求)。很公平,但AbstractItem是抽象的,所以传递给item的{​​{1}}始终是某种类型formatter ...

如何向编译器解释这一点?现在我的解决方案是使用AbstractItem ...

1 个答案:

答案 0 :(得分:6)

通过声明方法

public static Formatter<? extends AbstractItem> create()

您声明此方法的调用者永远不会知道Formatter的确切类型;调用者只知道它是Formatter<X> X AbstractItem或其子类的某些Formatter。因此,您无法将任何实例传递给返回的X,因为您永远不知道格式化程序可以处理哪个AbstractItem

这里禁止警告是绝对错误的,编译器会告诉你正确的事情:你的代码是不安全的。给定FooBarFormatter<Foo>两个子类,工厂可以返回Bar,您可以将format的实例传递给Formatter方法

存在语义问题,没有指示您的工厂将返回Formatter<X>的原因以及原因。要么让它成为一个具体工厂,返回? extends …,其中X是具体类型而不是public static <T extends AbstractItem> Formatter<T> create(Class<T> forItemType) ,或者你必须添加一个参数来提供工厂的提示,需要哪种格式化,例如

public static Formatter<AbstractItem> create()

或者你把它变成

Formatter

指定返回的AbstractItem可以处理所有种类的AbstractItem。请记住,您仍然可以将Formatter<AbstractItem>的任何子类传递给AbstractItem,因为AbstractItem的每个子类实例仍然是{{1}}的实例,就像在前仿制时代。