Placing objects of an inconsistent type in an initialized map - is it expected and legal?

Today I discovered that a put object can have an object that is in an existing Map , even if the object cannot be applied to the correct type.

First, let me start with a simple example:

 Map<Integer, String> myMap = new HashMap<>(); //plain old hashmap myMap.put(9,"star"); //no problem myMap.put(10, 1.2); //Incompatible type, the compiler yells Map<Integer, Double> aMap = (Map<Integer, Double>) myMap; //Cannot cast, the compiler yells 

So far, everything was expected, since you should not put an object of an inconsistent type in an already constructed map. Now consider the following:

 public class NoRulesForMe { static Object theRing; public static void main(String[] args){ Map<Integer, String> myMap = new HashMap<>(); myMap.put(9,"star"); Map<Integer, Double> myMapMorphed = castWildly(myMap); myMapMorphed.put(99, 3.14); System.out.println(myMapMorphed.get(9)); //"star", as we put in System.out.println(myMapMorphed.get(99)); //3.14, as we put in } public static <T> T castWildly(Object value){ theRing = value; T morphed = (T) theRing; return morphed; } } 

I am surprised that this did not cause a runtime error - how does Map do it, and this behavior is specified in the JLS or API and therefore depends on it?

The reason I'm asking for is because I saw a (more involved) version of this in the production code, and I wonder, even if it can be confusing and smelly, is it possible to guarantee that it will work functionally. Any input would be appreciated.

+5
source share
1 answer

This type of coding is very risky! Although it will compile, you will notice that the compiler complains about the warning:

Note. NoRulesForMe.java uses unverified or unsafe operations.
Note. Recompiling with -Xlint: unchecked for details.

These warnings, especially because you are using generics, should never be ignored or simply suppressed. You must absolutely be sure (logically following the code) that the roll is safe and will not cause some problems later. It's best to always code so that errors are detected and collected at compiler time instead of runtime. A warning given by the compiler tells you that everything may go wrong.

You pass myMap as an Object to castWildly , and when you cast, you drop from Object to Map .

The compiler can conclude that T in your code has the target type Map<String, Double> and, therefore, can do it. However, when casting, it has no information about which (sub) type is Object value (or Object theRing ). Thus, it has no way to verify that casting is safe (especially type safe).

A problem with this code occurs when you retrieve values ​​from your map. The following code has one extra line added, and the code compiles (with the same warning as above). This is because when retrieving a Double value from a map declared as Map<String, Double> , it is absolutely valid when the compiler checks the type ... at runtime, however, your code will work (a runtime failure error is displayed below ) This is a very dangerous coding method, especially in production code. You would rather have your compiler give you errors than deploying production code that compiles and crashes your product in real time.

 public class NoRulesForMe { static Object theRing; public static void main(String[] args){ Map<Integer, String> myMap = new HashMap<>(); myMap.put(9,"star"); Map<Integer, Double> myMapMorphed = castWildly(myMap); myMapMorphed.put(99, 3.14); System.out.println(myMapMorphed.get(9)); //"star", as we put in System.out.println(myMapMorphed.get(99)); //3.14, as we put in // added to show why this style of coding causes problems Double testValue1 = myMapMorphed.get(9); } public static <T> T castWildly(Object value){ theRing = value; T morphed = (T) theRing; return morphed; } } 

Runtime error when running the above code:

star
3.14
An exception in the "main" thread java.lang.ClassCastException: java.lang.String cannot be added to java.lang.Double on NoRulesForMe.main (NoRulesForMe.java:19)

For more information, read Joshua Bloch's "Effective Java"; Paragraph 24: Eliminate unverified warnings. (This item is under the heading Generics).

+1
source

Source: https://habr.com/ru/post/1272763/


All Articles