There's no way to create a Map<Class<...>, Parser<...>>
where the ...
-s can both be anything but have to match between a key and its value; so there's no way that you can get the compiler to do the checking for you, where retrieving a Class<T>
is guaranteed to give you a Parser<T>
. However, your code itself is correct; you know that your cast is correct, even though the compiler does not.
So, when you know that your cast is correct, but Java doesn't know it, what can you do?
The best and safest approach is to craft a specific piece of your code, as small as possible, that is responsible for handling the translation between checked and unchecked logic, and for making sure that the unchecked logic doesn't cause any mistakes. Then, you just mark that code with the appropriate @SuppressWarnings
annotation. For example, you can have something like this:
public abstract class Parser<T> {
private final Class<T> mType;
protected Parser(final Class<T> type) {
this.mType = type;
}
public final Class<T> getType() {
return mType;
}
@SuppressWarnings("unchecked")
public final <U> Parser<U> castToParserOf(final Class<U> type) {
if (type == mType) {
return (Parser<U>) this;
} else {
throw new ClassCastException("... useful message ...");
}
}
}
This would allow you to safely write, in your example:
public <T> void addParser(final Parser<T> parser) {
parsers.put(parser.getType(), parser);
}
private <T> Parser<T> parserFor(final Class<T> type) {
return parsers.get(type).castToParserOf(type);
}