0

My Java application is displaying some odd behaviour, and I'm having trouble finding a solution.

This code snippet takes the contents of a LinkedHashMap where the keys and values are both of type String and writes them to a text file. configWriter is of type PrintWriter.

for (Map.Entry<String, String> entry : configMap.entireSet()) {

    configWriter.println(entry.getKey() + "=" + entry.getValue());
}

The map was declared:

LinkedHashMap<String, String> configMap = new LinkedHashMap<String, String>();

It is populated by reading tokens from a text file using the Scanner class.

One of the values in the map is a String that contains only numbers (an integer), but when the loop gets to this value, it throws a ClassCastException saying that you cannot cast from Integer to String. This makes perfect sense, but the type of the values is String.

Is there a way to force Java to keep this value as a String?

apatrick
  • 194
  • 2
  • 14

2 Answers2

7

It's almost certain that rawtypes are getting used somewhere and there's an Integer sneaking its way into the map.

The easiest way to identify where an Integer is getting inserted into the map is to replace the initialization of configMap with

Map<String, String> configMap = Collections.checkedMap(
  new LinkedHashMap<String, String>(), String.class, String.class);

which will actually throw an exception if something other than a String gets inserted into the map. I wouldn't personally use that in production code, but this is probably the simplest way to "smoke out" the bug; you'll get an exception and a stack trace for where exactly the Integer is getting inserted.

As it stands, the way generics are implemented with type erasure means that there's nothing actually stopping non-String values from being inserted into the map. The compiler makes some effort to prevent it, but rawtypes and legacy code can get around it. Using Collections.checkedMap will force the issue, though.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
2

Louis Wasserman has explained the probable cause of your problems.

I just want to point out that there are a couple of flaws in your thinking in your question.

Why is Java automatically casting from String to Integer when reading LinkedHashMap

First, you can't cast from a String to an Integer. With Java reference types, you can only cast an object to a type if the object is an instance of that type, or a subtype of that type. The String type is not a subtype of Integer, so the operational semantics of the JVM forbid such a cast - you'd get a runtime exception if you wrote compilable code that tried to do this.

Second, there is nothing in LinkedHashMap ... or any other collections class which would convert an element/key/value instance from String to Integer without you telling it to. Well-behaved (Java) APIs just don't do that kind of thing.

Which brings us back to Louis's diagnosis - something (e.g. your code) has added that Integer object as a value. It is the kind of thing that can happen if you use raw types or suppress / ignore "unchecked conversion" warnings.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216