Understanding Generics in Object Initialization - java

I have a basic question about generics in Java: what is difference between the following two initializations of a map?
Map<String, String> maplet1 = new HashMap<String, String>();
Map<String, String> maplet2 = new HashMap();
I understand the the first initialization is specifying the generics in the object construction, but I don't understand the underlying ramifications of doing this, rather than the latter object construction (maplet2). In practice, I've always seen code use the maplet1 construction, but I don't understand where it would be beneficial to do that over the other.

The second Map is assigned to a raw type and will cause a compiler warning. You can simply use the first version to eliminate the warning.
For more see: What is a raw type and why shouldn't we use it?

The first one is type-safe.
You can shorthand the right side by using the diamond operator <>. This operator infers the type parameters from the left side of the assignment.
Map<String, String> maplet2 = new HashMap<>();

Lets understand the concept of Erasure. At RUNTIME HashMap<String, String>() and HashMap() are the same represented by HashMap.
The process of converting HashMap<String,String> to HashMap (Raw Type) is called Erasure.
Without the use of Generics , you have to cast , say the value in the Map , to String Explicitly every time.
The use of Generics forces you to Eliminate cast.
If you don't use Generics , there will be high probability that a Future Developer might insert another type of Object which will cause ClassCastException

Related

Need help on generic array creation

I have a line of code which compiles and works:
HashMap<String, Object>[] resultArray =
new ObjectMapper().readValue(json, HashMap[].class);
It uses Jackson ObjectMapper.
And I have another line, which gives Generic array creation error:
HashMap<String, Object>[] resultArray = new HashMap<String, Object>[] { resultObject };
I know that it is not possible to do this in Java. But obviously readValue() somehow did it. How can it be?
readValue created an array of the raw type, HashMap[] -- not of HashMap<String, Object>[]. Raw types are a relatively low-level, mostly-obsolete feature that you should try to avoid if possible; but with certain things, like reflection (which Jackson relies on heavily), it's often not possible.
The short of it is that Jackson is playing with a dangerous tool. As with many dangerous tools, it's not that it'll always cause damage; it's that it can cause damage, and should thus be used carefully and with some amount of experience.
The danger essentially boils down to the fact that with raw types, the compiler can't track the actual, parameterized types you want to work with; and that means it can't protect you from using them incorrectly.
At some point, you'll probably be casting this raw array to a generic one; something like:
HashMap[] original = new HashMap[0];
HashMap<String,Object>[] publiclyVisible = original;
// or
#SuppressWarnings("unchecked")
HashMap<String,Object> singleElement = original[0];
Because of erasure, there's actually nothing in the JVM that can track that publiclyVisible is meant to hold an array of HashMap<String, Object>s. All the JVM knows is that it holds an array of raw HashMaps. Thus, if you put a Map<Integer, Foo> into original, the JVM will let you; and if someone later retrieves it as a Map<String, Object>, the JVM will happily allow that, too. It's only when someone tries to use the types that they'll get into trouble, and it'll be at a very non-obvious way. For instance, maybe they'll do:
Map<String, Object> myMap = publiclyVisible[0];
for (String key : myMap.keySet()) {
...
... and they'll get a ClassCastException on that for line, saying that Integer can't be cast to String. This is very confusing! The problem is that the JVM has erased the type information, such that it couldn't track any of the improper array stuff. The first time it detects something is amiss is when it casts the reference of the first myMap key — which is an Integer, because this was accidentally a HashMap<Integer, Foo> — to a String. Essentially, the programmer expects myMap's keys to all be Strings, but erasure and raw types have conspired to allow them to be any type at all.
There are other variants of the problem, and other confusing ways to trigger it; but they all boil down to the same basic problem. In short, erasure means that the JVM can't track the actual types of your references, and raw types means the compiler can't track them either, and when you combine those them you can increase the likelihood of a confusing bug.

get() method of HashMap

I have a HashMap:
Map stuff = new HashMap<String, ArrayList<Thing>();
I am trying to use the .get() method to get the ArrayList, but I have an error message saying,
Object cannot be converted to ArrayList;
I'm not really sure why I am getting this error message? Also, is there another way to acquire the ArrayList at the key?
Try parameterizing the Map declaration with <>.
Map<String, ArrayList<Thing>> stuff = new HashMap<>() ;
Without the type declaration it will be an untyped Map, so the compiler will only be able to assume that the results of get are the most generic possible result, an Object.
With the diamond operator the compiler will infer the type of the right hand side from the left hand declaration.
You can cast the result of get back into an ArrayList, but it is better to parameterize the Map declaration instead. A cast will require runtime checks which have some performance cost and can potentially introduce crashes once your code becomes more difficult to understand. If you use the parameterized form instead, the compiler can prove that it is the right type, preventing crashes and running faster.
If you're using an old java from before java 1.7 you'll need to write out the full type in the <>.
See this tutorial from oracle for more information.

How do I implement a hashmap using generics? (Issue with not being able to create generic arrays) [duplicate]

In the Java implementation, I found
transient Entry[] table;
which is initiated in constructor as
table = new Entry[capacity];
I know and understand that creating generic array is not allowed but then what I fail to understand is that how the whole thing works. I mean when we do something like
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
How does above codes leads to creating an Entry array of type <Integer, String>
Well, few people are not able to understand what I am asking. To rephrase what I am asking is what is the point in doing something like
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
When it does not result in
Entry<Integer, String>
Generics are a compile-time safety. At runtime, the map only know about Objects. This is known as type erasure. To scare you even more, the following code will run without problem:
Map<Integer, Integer> safeMap = new HashMap<>();
Map unsafeMap = safeMap;
unsafeMap.put("hello", "world");
You'll get a warning at compile time, because you're using a raw Map instead of a generic one, but at runtime, no check is done at all, because the map is a good old map able of storing any object. Only the compiler prevents you from adding Strings in a map or integers.
The implementation makes an array of Entry<K,V> objects of type
static class Entry<K,V> implements Map.Entry<K,V>
without providing generic type parameters (source). This is allowed, but it comes with understanding that the compiler is no longer guarantees type safety. For example, in other places in code you could write
Entry<K,V> e = table[bucketIndex];
and the compiler will let you do that. If you know for sure that you always set elements of table[] to null or Entry<K,V>, then you know that the assignment is correct.
The reason this works without a problem is that generic types in Java are implemented through type erasure, i.e. there is no difference at runtime between Entry<K,V> objects Entry<Integer,Integer> and Entry<String,Long>.
Try to think of Java Generics this way: type parameters only apply to the static type of reference-typed expressions and do not apply to the type of actual instances being referred to by the reference values at runtime.
I find the above key to developing the proper intuitions when reading Java code. So the next time you see
new HashMap<Integer, String>()
read it as follows: "This is an instance creation expression of the type HashMap<Integer, String>. At runtime this expression will yield a reference to an instance of the HashMap class." As long as the compiler can precisely track what you do with the result of that expression, it can maintain the knowledge that this is indeed a HashMap<Integer, String>, but no further than that.
Now, since the static type system is not powerful enough to track the type parameters on the component type of arrays (the fact that Java's array types are covariant plays strongly here), the code is forced to break out of the static type safety network. The key observation is that on its own, this does not make the code incorrect, it only constrains the power of the compiler to find programming mistakes. This is why Java allows you to make unchecked casts from raw into generic types, although not without a warning which marks the spot where you have left the provinces of static type safety.

Generics in HashMap implementation

In the Java implementation, I found
transient Entry[] table;
which is initiated in constructor as
table = new Entry[capacity];
I know and understand that creating generic array is not allowed but then what I fail to understand is that how the whole thing works. I mean when we do something like
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
How does above codes leads to creating an Entry array of type <Integer, String>
Well, few people are not able to understand what I am asking. To rephrase what I am asking is what is the point in doing something like
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
When it does not result in
Entry<Integer, String>
Generics are a compile-time safety. At runtime, the map only know about Objects. This is known as type erasure. To scare you even more, the following code will run without problem:
Map<Integer, Integer> safeMap = new HashMap<>();
Map unsafeMap = safeMap;
unsafeMap.put("hello", "world");
You'll get a warning at compile time, because you're using a raw Map instead of a generic one, but at runtime, no check is done at all, because the map is a good old map able of storing any object. Only the compiler prevents you from adding Strings in a map or integers.
The implementation makes an array of Entry<K,V> objects of type
static class Entry<K,V> implements Map.Entry<K,V>
without providing generic type parameters (source). This is allowed, but it comes with understanding that the compiler is no longer guarantees type safety. For example, in other places in code you could write
Entry<K,V> e = table[bucketIndex];
and the compiler will let you do that. If you know for sure that you always set elements of table[] to null or Entry<K,V>, then you know that the assignment is correct.
The reason this works without a problem is that generic types in Java are implemented through type erasure, i.e. there is no difference at runtime between Entry<K,V> objects Entry<Integer,Integer> and Entry<String,Long>.
Try to think of Java Generics this way: type parameters only apply to the static type of reference-typed expressions and do not apply to the type of actual instances being referred to by the reference values at runtime.
I find the above key to developing the proper intuitions when reading Java code. So the next time you see
new HashMap<Integer, String>()
read it as follows: "This is an instance creation expression of the type HashMap<Integer, String>. At runtime this expression will yield a reference to an instance of the HashMap class." As long as the compiler can precisely track what you do with the result of that expression, it can maintain the knowledge that this is indeed a HashMap<Integer, String>, but no further than that.
Now, since the static type system is not powerful enough to track the type parameters on the component type of arrays (the fact that Java's array types are covariant plays strongly here), the code is forced to break out of the static type safety network. The key observation is that on its own, this does not make the code incorrect, it only constrains the power of the compiler to find programming mistakes. This is why Java allows you to make unchecked casts from raw into generic types, although not without a warning which marks the spot where you have left the provinces of static type safety.

Eclipse Warning with Java HashMap

Eclipse is saying "HashMap is a raw type" When I use the following code
HashMap = new HashMap();
Any idea what could be wrong?
Eclipse will give you that warning when you use a non-Generic HashMap using Java 5 or newer.
See Also: The Generics Lesson in Sun's Java Tutorials.
Edit: Actually, here, I'll give an example too:
Say I want to map someone's name to their Person object:
Map<String, Person> map = new HashMap<String, Person>();
// The map.get method now returns a Person
// The map.put method now requires a String and a Person
These are checked at compile-time; the type information is lost at run-time due to how Java implements Generics.
Nothing wrong exactly, but you are missing out on the wonderful world of generics. Depending on what constraints you want to place on the types used in your map, you should add type parameters. For example:
Map<String, Integer> map = new HashMap<String, Integer>();
That is missing generics, i.e. . If you don't know thise then set the eclipse compiler to java 1.4
Try
HashMap<String,Integer> map = new HashMap<String,Integer>();
instead (obviously replacing the key type (String) and value type (Integer)).
That usually means you're mixing generic code with non-generic code.
But as your example wont even compile its rather hard to tell....
It's missing the generic type. You should specify the key-value generic pair for your map. For instance, the following is a declaration that instantiates a HashMap with String type key and Integer type value.
Map<String, Integer> map = new HashMap<String, Integer>();
All of these are valid answers, you could also use the #SurpressWarnings annotation to get the same result, without having to resort to actual generics. ;)
hashmap is a raw type and hence should be parameterised ie
what ever the data we get through the haspmap function their type must be declared for getting its functions
for example
HashMap<String, Integer> map = new HashMap<String, Integer>();
With the latest Java, you do not have to explicitly mention the variable types in declaration. You can simply put:
= new HashMap<>();

Categories

Resources