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