Search code examples
javacollectionstype-conversiontype-erasure

Type erasure in Java


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.


Solution

  • 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.