Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics vs. Factory

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

like image 253
vektor Avatar asked Nov 20 '13 13:11

vektor


People also ask

Is it a good idea to use generics in collections?

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.

What are generics in Java?

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.

What is the biggest advantage of generics?

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.


1 Answers

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.

like image 138
Holger Avatar answered Oct 31 '22 05:10

Holger