I had to process nested collections (for example, a map of lists or a map of maps) and that is the way I was going to handle it:
public static String doSomething(Map<?, ? extends Collection<?>> map) {
...
}
public static String doSomething(Map<?, ? extends Map<?, ?>> map) {
...
}
But I am told by the compiler that the above two methods have the same type erasure. I wonder why, since I have specified different type bounds.
The erasure of Map<?, ? extends Collection<?>>
is Map<Object, Object>
The erasure of Map<?, ? extends Map<?, ?>>
is also Map<Object, Object>
To understand why, you need to understand how how the erasure of Map
is calculated. Basically, you take the type (Map<K, V>
) and replace the formal type parameters (not the actual type parameters) with their respective least upper bound types. In this case, the least upper bound type is Object
for both K
and V
, since neither have any type constraints on them ... in the Map
interface.
I think I might have made up the term "least upper bound type". (Sorry) But what I mean is the most specific type that is not a subtype of any of the possible types in the set that are allowed.
Another way to think of erasure is as follows. Consider this class:
public class Test <T> {
public set(T t):
}
Now imagine that we had to express that without using generics. What actual type would we use in place of T
? In this case, it would be Object
.
And in fact, when a generic type is mapped to a runtime type, that is exactly what happens!
But, basically, you won't be able to create overloads of a method that differ only on the type parameterization of a Map
type. Unless you reify the types:
public class X implements Map<String, Integer> ...
public class Y implements Map<String, Double> ...
public static String doSomething(X map) {
...
}
public static String doSomething(Y map) {
...
}
.... which is ugly, to say the least.
Solution: use different method names instead of trying to overload the same name.