If you use something like Guava's TypeToken
then you can do this in a type-safe way but still unchecked.
class ExpressionMap {
private final Map<TypeToken<?>, Function<?, Expression>> m =
new HashMap<>();
<T> void put(TypeToken<T> type, Function<T, Expression> f) {
m.put(type, f);
}
<T> Function<T, Expression> get(TypeToken<T> type) {
@SuppressWarnings("unchecked")
final Function<T, Expression> f =
(Function<T, Expression>) m.get(type);
return f;
}
}
static ExpressionMap stuff = ExpressionMap();
static {
stuff.put(new TypeToken<List>() {}, ListExpression::new);
stuff.put(new TypeToken<String>() {}, StringExpression::new);
}
You can use a Class
instead of TypeToken
but the problem is that it breaks down with generic types.
If you had a
ListStringExpression extends Expression {
ListStringExpression(List<String> l) {}
}
you can't have a Class<List<String>>
so all ListOfSomeTypeExpression::new
get lumped together as Function<List, Expression>
. It's not safe.
You could do this:
ExpressionMap em = new ExpressionMap();
em.put(List.class, ListStringExpression::new);
// causing heap pollution
// subtly passing List<Integer> to List<String>
Expression ex =
em.get(List.class).apply(Arrays.asList(1, 2, 3));
So it's possible but beware the caveats.
Also see What is a raw type and why shouldn't we use it?