Java raw reference to parameterized type doesn't respect parameter

Viewed 120

I was experimenting with various ways of instantiating a HashMap and there's a problem I've been facing. One would be able to choose between the following, when creating a map from String to String:

Map<String, String> map = new HashMap<String, String>();
Map<String, String> map = new HashMap<>();
Map<String, String> map = new HashMap();

The benefit of using the first one, I thought, would be to bind the overall object in memory to the String type for both key and value, since the reference(map) would point to the actual object of type HashMap<String, String>

Running the following code throws no error and displays reasonable output:

Map<String, String> map = new HashMap<String, String>();
map.put("hi", "there");
Map map2 = map;
map2.put(5, 4);
System.out.println(map.get("hi"));
System.out.println(map2.get("hi"));
System.out.println(map2.get(5));
System.out.println(map2.get(5).getClass());
System.out.println(map.size());

the output is:

there
there
4
class java.lang.Integer
2

We can clearly see that both references point to the same object in memory, which I thought would be of type HashMap<String, String>, but we can add any subtype of Object, when the reference is not parameterized.

My question is, since the parameterized constructor does not matter for the object stored in memory, why would we ever want to repeat the types on the right side? Why write this:

Map<String, String> map = new HashMap<String, String>();

instad of this:

Map<String, String> map = new HashMap();

even if it has the same effect?

1 Answers

Due to type erasure, the generated code doesn’t differ. All that Generics is about, is to get compiler feedback about the correctness of the code. Only when your code is free of “unchecked” and “raw types” warnings, you can be sure that no “heap pollution”, i.e. stored objects of incorrect type, will occur.

In this regard, the line

Map<String, String> map = new HashMap();

is problematic because it generates compiler warnings. Of course, when you look at it and have enough understanding about he implications, you will know that it can’t cause any harm.

In contrast, the similar line

Map<String, String> map = new HashMap(oldMap);

does allow to initialize the map with incorrect contents. Of course, you could now check the type of oldMap, to see that it is compatible, and be again confident that this specific case is ok. But this approach defeats the entire purpose of Generics, to let the compiler check this for you, which you can easily achieve by inserting the <>.

In general, when you have a large codebase, you want to have it entirely warning free, so you do not have to go at each location, to see whether it can be considered harmless, even if it only requires a short look.


The necessity to insert <> is connected to the history. When Java 5 was released, the default for all method invocations was to perform the generic check, so it’s not convincing to say that “compatibility concerns” were the reason to make raw type the default for constructor invocations. My guess is, the javac developers had difficulties with the implementation of the type inference and there was release time pressure.

Note that with Java 5 and Java 6, the developer even had to insert the full type explicitly for each generic instantiation.

With Java 7, the problem was mitigated by introducing the “diamond operator”, <>, to let the compiler infer the generic types, as it should have done in the first place. It’s still necessary to request the inference explicitly, though, for “compatibility”, despite, as said, for method invocations, there is no opt-in mechanism and it works since day one of Generics. To me, the raw type default is a design mistake.

But still, for practical purposes, as said above, you want a warning free application with the compiler performing the checks, hence, you should always insert the necessary <>.

Since Java 10, you can declare local variables like

var map = new HashMap<String, String>();

eliminating all redundancy in the declaration.

Related