Search code examples
javagenericspolymorphismtype-parameter

Java Generics: "Nested" type parameters?


EDIT: Yes, this may be a duplicate. But whereas the other question is akin to "What is a chainsaw and how do I use it?" mine is more like "I am trying to drill a hole with this machine here and it doesn't work - what's up?". Of course the answer is "Don't use a chainsaw!" and easily found once you know you are dealing with a chainsaw.

But I didn't even know that my question was related to "raw types vs. wild cards" and therefore didn't find that question - so maybe this question is still useful for others like me.

ORIGINAL QUESTION: Let's say I have the following data structure which represents an item in my user interface:

public static abstract class RowItem<T> {

    public final T value;

    public RowItem(T value) {
        this.value = value;
    }
}

Now, I would like to do the following:

public static abstract class EpgRowItem<T> extends RowItem<Pair<String, T>> {

    public EpgRowItem(Pair<String, T> value) {
        super(value);
    }
}

public static final class EpgRowProgramItem extends EpgRowItem<Program> {

    public EpgRowProgramItem(Pair<String, Program> value) {
        super(value);
    }
}

public static final class EpgRowOtherDateItem extends EpgRowItem<LocalDate> {

    public EpgRowOtherDateItem(Pair<String, LocalDate> value) {
        super(value);
    }
}

So, in words: An EpgRowItem is a RowItem that contains a Pair, of which the first member is always a String and the second member can be anything. Furthermore, an EpgRowProgramItem is an EpgRowItem in which the second member of the pair is a Program. Likewise, an EpgRowOtherDateItem is an EpgRowItem in which the second member of the pair is a LocalDate.

This seems to work until I have this at some other place in my code:

List<OverlayPresenter.EpgRowItem> programs = ...;
OverlayPresenter.EpgRowItem epgRowItem = programs.get(0);
String channelId = epgRowItem.value.first; // DOESN'T COMPILE?!

I feel the compiler should know that epgRowItem.value must ALWAYS be a Pair<String, ?>, and consequently epgRowItem.value.first must ALWAYS be a String.

Actually, it doesn't even seem to know the first part, i. e. the following doesn't compile either:

Pair<String, ?> pair = epgRowItem.value; // epgRowItem.value is an Object?!

What am I doing wrong? Am I just asking too much of Java's generics?


Solution

  • You get into trouble because you are using the raw type EpgRowItem (a raw type is a parameterized type for which you do not specify type parameters; these exist because of backward compatibility with Java 1.4 and older):

    List<OverlayPresenter.EpgRowItem> programs = ...;
    OverlayPresenter.EpgRowItem epgRowItem = programs.get(0);
    

    See: What is a raw type and why shouldn't we use it?

    Use a type parameter, or at least a wildcard:

    List<OverlayPresenter.EpgRowItem<?>> programs = ...;
    OverlayPresenter.EpgRowItem<?> epgRowItem = programs.get(0);
    String channelId = epgRowItem.value.first;  // OK