7

Setup:

I have an interface for some formatters:

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

I have a factory creating such formatters:

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){...}};
    }

Now I use this factory to obtain the formatter & I use it:

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

The items list contains only AbstractItem subtypes that the formatter is able to process.

Problem:

I am getting two warnings:

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.

OK, so I try to fix the first one: I know that the factory returns something descended from AbstractItem:

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

Now the warning on Line 1 disappears, but a new error on Line 3 occurs:

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

So if I understand correctly, it's complaining that AbstractItem is not a sub-type of AbstractItem (as required in the type constraint <? extends AbstractItem>). Fair enough, but AbstractItem is abstract, so the item I pass to the formatter is always of some type extending AbstractItem...

How do I explain this to the compiler? For now my solution is to go with the @SuppressWarnings...

vektor
  • 3,312
  • 8
  • 41
  • 71
  • 2
    maybe I'm missing something, but why is your interface generic at all? Your factory does not seem to be able to create specialized formatters for subclasses of AbstractItem anyway. So why not just `interface AbstractItemFormatter { String format(AbstractItem item); }` ? – Paolo Falabella Nov 20 '13 at 13:34
  • @Paolo Falabella: that’s true for this specific factory method but maybe there ought to be more than this way to create a formatter – Holger Nov 20 '13 at 13:39
  • I have expanded the Factory code. – vektor Nov 20 '13 at 13:39
  • Very similar: [Strategy Design Pattern, Generics and TypeSafety](http://stackoverflow.com/questions/19908963/strategy-design-pattern-generics-and-typesafety) – Paul Bellora Nov 20 '13 at 14:09

1 Answers1

6

By declaring the method

public static Formatter<? extends AbstractItem> create()

you are declaring that the caller of this method will never know the exact type of the Formatter; the caller will only know that it is some Formatter<X> where X is AbstractItem or a subclass of it. So you can’t pass any instance to the returned Formatter as you never know which X the formatter can handle.

Suppressing the warning here is absolutely wrong, the compiler tells you the right thing: your code is unsafe. Given two subclasses of AbstractItem, Foo and Bar the factory could return a Formatter<Foo> and you could pass an instance of Bar to its format method.

There’s the semantic problem that there is no indicator for what Formatter your factory will return and why. Either you make it a concrete factory returning a Formatter<X> where X is a concrete type instead of ? extends … or you have to add a parameter to provide a hint to the factory, which kind of formatter is required, e.g.

public static <T extends AbstractItem> Formatter<T> create(Class<T> forItemType)

Or you turn it into

public static Formatter<AbstractItem> create()

specifying that the returned Formatter can handle all kinds of AbstractItem. Just remember that you still can pass any subclass of AbstractItem to a Formatter<AbstractItem> as every instance of a subclass of AbstractItem is still an instance of AbstractItem too, just like in the pre-Generics times.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 1
    I forgot one other solution: the factory itself could have a type parameter as well so a `Factory` can return a `Formatter` – Holger Nov 20 '13 at 13:54