You can't declare a map in this way. Basically, Java does not have an expressive-enough type system for this.
But luckily, the language provides an escape hatch from using the type system, in the form of casts. Basically, casts are a way to provide type information that the compiler doesn't have; the onus is then on you, however, to make sure that it actually is type safe.
Firstly, declare the map with wildcard types:
private final Map<A<?>,B<?>> map;
Then, only put key/value pairs into the map which do meet the constraint:
<T> void put (A<T> key, B<T> value) {
map.put(key, value);
}
And then cast when you get the element out again:
@SuppressWarnings("unchecked") // Safe
<T> B<T> get(A<T> key) {
return (B<T>) map.get(key);
}
The point is that you can know more about the types than the compiler. Provided you take care to only put in safely-castable pairs, it is safe to cast them. (You also need to ensure that the equals
method takes the type into account, so no A<T>
equals A<S>
unless S == T
).
This is basically a "type-safe heterogeneous container", as described in Effective Java (Item 33 in 3rd Edition).
I am wondering if there is a more elegant way that does not require me to cast every value I retreive.
For one thing, you aren't actually doing a cast in the get
method at runtime: that's what an unchecked cast means.
For another, generics introduces loads of casts - basically generics is just a way to avoid inserting casts manually. So if there were a problem with the casts (such as performance), you'd already observe this in lots of places.
To paraphrase Kubrick: stop worrying, and love the cast.