0

Assume I have this code:

private Hashtable items = new Hashtable();

Eclipse would throw a warning for such assignment, because it is an unchecked, unsafe operation.

A much better solution would be either:

private Hashtable<String,String> items = new Hashtable<String,String>();

or (if I'm not sure about the type)

private Hashtable<?,?> items = new Hashtable<?,?>();

What are the consequences of not providing type explicitly and can this be abused from, let's say, security point of view? Can this be caught in a test (do I have to use reflection?) and how?

achudars
  • 1,486
  • 15
  • 25
  • "*Eclipse would throw a warning for such assignment, because it is an unchecked, unsafe operation"* The Java compiler would throw a raw type warning, not an unchecked/unsafe operation. – m0skit0 Mar 18 '15 at 12:00

2 Answers2

1

By using a raw type, you can't have the compiler prevent you from adding any old key and value into the map. That might be fine if your keys and values happen to be Objects, but that's not a very common thing in my experience.

By using explicit type bounds, your code won't compile if you try to pass in parameters of the wrong type. For instance:

Map<String, String> genericMap = new HashMap<>();
genericMap.put(new Object(), new Object());  // compiler error.

However, if you were to write:

Map rawMap = genericMap;
rawMap.put(new Object(), new Object());  // fine.

So now basically all bets are off as to what is contained in that map. If you pass genericMap to a distant method which expected a Map<String, String> and is going to consume values from it, you are going to get a runtime failure, probably a ClassCastException:

import java.util.HashMap;
import java.util.Map;

class DontDoThis {
  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
    Map<String, String> genericMap = new HashMap<>();
    Map rawMap = genericMap;
    rawMap.put(new Object(), new Object());
    useMap(genericMap);
  }

  private static void useMap(Map<String, String> map) {
    for (String key : map.keySet()) {} // ClassCastException.
  }
}

If you use wildcard bounds, you can't do things like put new values into the map:

Map<?, ?> wildcardMap = genericMap;
genericMap.put("key1", "value1");  // fine.
wildcardMap.put("key", "value");   // compiler error.

You can, however, still use the values from wildcardMap; all you can know about them is that the values are subtypes of Object:

for (Map.Entry<?, ?> entry : wildcardMap.entrySet()) {
  Object key = entry.getKey();
  Object value = entry.getValue();

  // Use key and value somehow.
}

As to the question of how it can be caught in a test - you shouldn't write tests for this kind of stuff - successful compilation is your test!

The kind of coverage that compile-time type safety will give you is substantially greater, with substantially less code, than any test you could practically hope to write.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

Generics in Java are a compile time concept, they don't exist at runtime. This is called type erasure. Therefore from a security point of view, they cannot be abused in a running application. I don't know exactly what you want to check with reflection in a test, but you can only get generics information with reflection in a handful of cases.Reflection generics

Erwin de Gier
  • 632
  • 7
  • 12