david.mihola david.mihola - 1 month ago 17
Java Question

Java Generics: "Nested" type parameters?

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?

Answer

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