Java generics and factories
Establish:
I have some formatter interfaces:
interface Formatter<T extends AbstractItem> { String format(T item); }
I have a factory to create such a formatter:
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 get the formatter & I use it:
1: Formatter formatter = Factory.create(); 2: for (AbstractItem item : items) { 3: formatter.format(item); 4: }
The item list contains only abstractitem subtypes that the formatter can handle
Question:
I received 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 the descendants of abstractitem returned by the factory:
1: Formatter<? extends AbstractItem> formatter = Factory.create();
Now the warning on line 1 disappears, but a new error appears on line 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).
Therefore, if I understand it correctly, it complains that abstractitem is not a subtype of abstractitem () in type constraints Fair enough, but abstractitem is abstract, so the items I pass to the formatter always extend abstractitem of some kind
How do I explain this to the compiler? Now my solution is to use @ suppresswarnings
Solution
By declaring methods
public static Formatter<? extends AbstractItem> create()
The caller who declares this method will never know the exact type of formatter; The caller will only know that it is a formatter < x > Where x is abstractitem or its subclass Therefore, you cannot pass any instance to the returned formatter because you never know which X
It is absolutely wrong to cancel the warning here. The compiler tells you the right thing: your code is unsafe Given two subclasses of abstractitem, Foo and bar, the factory can return formatter < foo > and you can pass an instance of bar to its format method
There is a semantic problem and does not indicate what formatter your factory will return and why Either make it a concrete factory, or return formatter < x > where x is a concrete type instead of? Extension... Or you must add a parameter to provide a hint to the factory about which formatter is required, such as
public static <T extends AbstractItem> Formatter<T> create(Class<T> forItemType)
Or you turned it into
public static Formatter<AbstractItem> create()
Specifies that the returned formatter can handle various abstractitems Remember, you can still pass any subclass of abstractitem to formatter < abstractitem > because each instance of the subclass of abstractitem is still an instance of abstractitem, just like in the era before generics