Generics in Java were introduced as one of the features in JDK 5. It was one of the major changes in Java.
In Java 4, we had raw types only:
Java 5 introduced generics:
As you can see, the generics would imply the type of the List. In Java, Generic types or methods differ from raw types in the sense that they have type parameters such as above example. You may pass in the Type or wildcard instead of “Integer” in the above example. According to the JLS (4.8 Raw Types):
“The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.”
Sun decided to start Java development in 1990 led by James Gosling. Since then, there had been numerous changes to get to Generics which was introduced in J2SE 5.0 in 2004. J2SE 5.0 consists of a bunch of other Evolutionary Changes. There had been a couple of other changes after J2SE 5.0 was introduced. However, J2SE 5.0 is probably the most notable change in the Java history to be mentioned.
Wikipedia says, Generics provides compile-time (static) type safety for collections and eliminates the need for most typecasts (type conversion) (specified by JSR 14). You might be thinking so what? Let’s take a look at the benefits.
Code that uses generics has many benefits over the non-generic code. Here are 3 points to address this:
(String) list.get(0). Now we can replace that by enforcing the list to be String,
List<String> list = new ArrayList<String>();
Basically, the most important point above is probably type checking of parameters in your code at the compile time, automatically! This is very important as Casting was a big burn as most of the errors were bypassing the compiling until runtime exception rises in the production. Generics basically added stability to your code by making more of your bugs detectable at the compile time (Checked exception).
There are 2 important terms to discuss the works of Generics in Java:
This is basically saying as long as types are all passed compiler with Generics, ClassCastException should not occur at runtime. This is guaranteeing cast error-prone at the runtime as I discussed as the most important point in “Why Generics”.
Example of Type Safety:
This is basically saying that any of these type information added using Generics in the code will be removed in compiled bytecode. This is required to support old Java as Java itself is always backward compatible. Byte-level code is basically kept using old Java syntax which wouldn’t use the Generics.
Here is an example of Type Erasure. Basically above Generics Type Safety example is converted like this:
It is worth to note that the String insertion will not be at the bytecode level as that code block will never bypass Compiler (cast checked exception)
Also, Type Erasure introduces a problem like following:
What this compares at the end of a day is ArrayList, because Types would have been Erased. There are other limitations to Generics. See end of this page, you will find examples of them.
There are conventions Java developers like to follow when it comes to using Generics. By convention, type parameter names are single, uppercase letters. This follows Java Naming Convention that most of Java developers would know about. Naming conventions make the code clean when everyone is following the same convention.
Commonly used type parameter names follow:
You’ll see these names used throughout the Java SE API.
The question mark (?) is used as the wildcard in Generics, which represents an unknown type. A wildcard parameterized type is an instantiation of a generic type where at least one type argument is a wildcard.
Having a wildcard can mean different things in different context:
correct usage of wildcard:
incorrect usage of wildcard:
If you look closely to the usage of generics in the diamond brackets, the object is being defined using
super. There is a rule to define how and when to use
PECS stands for Producer-Extends, Consumer-Super. Let’s take an example of how they are used:
You want to go through the collection and do things with each item. Then the list is a producer, so you should use a Collection<? extends Thing>. The reasoning is that a Collection<? extends Thing> could hold any subtype of Thing, and thus each element will behave as a Thing when you perform your operation. (You actually cannot add anything to a Collection<? extends Thing>, because you cannot know at runtime which specific subtype of Thing the collection holds. To genericfy this, you would do Collection<? extends T>
You want to add things to the collection. Then the list is a consumer, so you should use a Collection<? super Thing>. The reasoning here is that unlike Collection<? extends Thing>, Collection<? super Thing> can always hold a Thing no matter what the actual parameterized type is. Here you don’t care what is already on the list as long as it will allow a Thing to be added; this is what? super Thing guarantees.
Primitives are not allowed as part of Type.
Above code example cannot be used like this:
It is to be used like this with values being autoboxed:
Static fields are not allowed
Creating an instance of Type is not allowed
Exceptions cannot be generic-fied
Cannot be used as Casts or
Above can be used with a wildcard like so:
You cannot create an array of parameterized types
You cannot have the same method signature of erasure form
Both result in Same strSet in the bytecode which results in a compile error, java: name clash: print(java.util.Set
Generics enable the use of stronger type-checking, the elimination of casts, and the ability to develop generic algorithms. Without generics, many of the features that we use in Java today would not be possible.
Do not use Raw types. Use Generics. Also, use Generics (types or methods) when you can. It is preferred way in modern Java. Also, upon using Generics, there cases where it is inevitable to avoid the unchecked warning. In that case, use @SuppressWarnings(“unchecked”) annotation and explain why it is safe to ignore this error.