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.
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
...
By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.
Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.
One of the big advantages of generics is performance. Using value types with non – generic collection classes results in boxing and unboxing when the value type is converted to a reference type and vice versa. Type Safety : Another feature of generics is type safety.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With