Java泛型与工厂

vek*_*tor 7 java generics factory

建立:

我有一些格式化程序的接口:

interface Formatter<T extends AbstractItem> {
  String format(T item);
}
Run Code Online (Sandbox Code Playgroud)

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

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){...}};
    }
Run Code Online (Sandbox Code Playgroud)

现在我使用这个工厂来获取格式化程序并使用它:

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

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.
Run Code Online (Sandbox Code Playgroud)

好的,所以我尝试修复第一个:我知道工厂返回的东西来自AbstractItem:

1: Formatter<? extends AbstractItem> formatter = Factory.create();
Run Code Online (Sandbox Code Playgroud)

现在第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).
Run Code Online (Sandbox Code Playgroud)

因此,如果我理解正确,它抱怨AbstractItem不是子类型AbstractItem(在类型约束中需要<? extends AbstractItem>).公平,但是AbstractItem是抽象的,所以item我传给的formatter是总是某种类型延伸AbstractItem...

我该如何向编译器解释这个?现在我的解决方案就是@SuppressWarnings......

Hol*_*ger 6

通过声明方法

public static Formatter<? extends AbstractItem> create()
Run Code Online (Sandbox Code Playgroud)

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

在这里取消警告绝对是错误的,编译器会告诉你正确的事情:你的代码是不安全的.鉴于两个子类AbstractItem,FooBar工厂可以返回Formatter<Foo>,你可以通过实例Barformat的方法.

存在语义问题,没有指示Formatter您的工厂将返回什么以及为什么返回.要么让它成为一个具体工厂,返回Formatter<X>X是具体类型而不是? extends …或者你必须添加一个参数来提供工厂的提示,需要哪种格式化器,例如

public static <T extends AbstractItem> Formatter<T> create(Class<T> forItemType)
Run Code Online (Sandbox Code Playgroud)

或者你把它变成了

public static Formatter<AbstractItem> create()
Run Code Online (Sandbox Code Playgroud)

指定返回的Formatter可以处理所有类型的AbstractItem.请记住,您仍然可以将任何子类传递AbstractItem给a,Formatter<AbstractItem>因为子类的每个实例AbstractItem仍然是一个实例AbstractItem,就像在Generics之前的时间一样.