Generics
Tags: java, programming, learning, beginners
Published: 2024-07-04 17:39:18

Generics enable you to detect errors at compile time rather than at runtime. You have used a generic class **ArrayList** and generic interface **Comparable**. _Generics_ let you parameterize types. With this capability, you can define a class or a method with generic types that the compiler can replace with concrete types. For example, Java defines a generic **ArrayList** class for storing the elements of a generic type. From this generic class, you can create an **ArrayList** object for holding strings and an **ArrayList** object for holding numbers. Here, strings and numbers are concrete types that replace the generic type.

The key benefit of generics is to enable errors to be detected at compile time rather than at runtime. A generic class or method permits you to specify allowable types of objects that the class or method can work with. If you attempt to use an incompatible object, the compiler will detect that error.

We'll explains how to define and use generic classes, interfaces, and methods and demonstrates how generics can be used to improve software reliability and readability.

## Motivations and Benefits

The motivation for using Java generics is to detect errors at compile time. Java has allowed you to define generic classes, interfaces, and methods since JDK 1.5. Several interfaces and classes in the Java API were modified using generics. For example, prior to JDK 1.5 the **java.lang.Comparable** interface was defined as shown in Figure below (a), but since JDK 1.5 it is modified as shown in Figure below (b).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdhhcqjypapu6dzz183q.png)

Here, **<T>** represents a _formal generic type_, which can be replaced later with an _actual concrete type_. Replacing a generic type is called a _generic instantiation_. By convention, a single capital letter such as **E** or **T** is used to denote a formal generic type.

To see the benefits of using generics, let us examine the code in Figure below. The statement in Figure below (a) declares that **c** is a reference variable whose type is **Comparable** and invokes the **compareTo** method to compare a **Date** object with a string. The code compiles fine, but it has a runtime error because a string cannot be compared with a date.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yuzo54lk3g1mfm196vo0.png)

The statement in Figure above (b) declares that **c** is a reference variable whose type is **Comparable<Date>** and invokes the **compareTo** method to compare a **Date** object with a string. This code generates a compile error, because the argument passed to the **compareTo** method must be of the Date type. Since the errors can be detected at compile time rather than at runtime, the generic type makes the program more reliable.

The **ArrayList** Class has been a generic class since JDK 1.5. Figure below shows the class diagram for **ArrayList** before and since JDK 1.5, respectively.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apm322c96pcckurwaunc.png)

For example, the following statement creates a list for strings:

`ArrayList<String> list = new ArrayList<>();`

You can now add _only strings_ into the list. For instance,

`list.add("Red");`

If you attempt to add a nonstring, a compile error will occur. For example, the following statement is now illegal, because **list** can contain only strings.

`list.add(new Integer(1));`

Generic types must be reference types. You cannot replace a generic type with a primitive type such as **int**, **double**, or **char**. For example, the following statement is wrong:

`ArrayList<int> intList = new ArrayList<>();`

To create an **ArrayList** object for **int** values, you have to use:

`ArrayList<Integer> intList = new ArrayList<>();`

You can add an **int** value to **intList**. For example,

`intList.add(5);`

Java automatically wraps **5** into **new Integer(5)**. This is called _autoboxing_, as introduced in [Automatic Conversion between Primitive Types and Wrapper Class Types](https://dev.to/paulike/automatic-conversion-between-primitive-types-and-wrapper-class-types-46en).

Casting is not needed to retrieve a value from a list with a specified element type, because the compiler already knows the element type. For example, the following statements create a list that contains strings, add strings to the list, and retrieve strings from the list.

`1 ArrayList<String> list = new ArrayList<>();
2 list.add("Red");
3 list.add("White");
4 String s = list.get(0); // No casting is needed`

Prior to JDK 1.5, without using generics, you would have had to cast the return value to **String** as:

`String s = (String)(list.get(0)); // Casting needed prior to JDK 1.5`

If the elements are of wrapper types, such as **Integer**, **Double**, and **Character**, you can directly assign an element to a primitive type variable. This is called _autounboxing_, as introduced in the link above. For example, see the following code:

`1 ArrayList<Double> list = new ArrayList<>();
2 list.add(5.5); // 5.5 is automatically converted to new Double(5.5)
3 list.add(3.0); // 3.0 is automatically converted to new Double(3.0)
4 Double doubleObject = list.get(0); // No casting is needed
5 double d = list.get(1); // Automatically converted to double`

In lines 2 and 3, **5.5** and **3.0** are automatically converted into **Double** objects and added to **list**. In line 4, the first element in **list** is assigned to a **Double** variable. No casting is necessary, because **list** is declared for **Double** objects. In line 5, the second element in **list** is assigned to a **double** variable. The object in **list.get(1)** is automatically converted into a primitive type value.