When we write classes in Java, we need to be able to specify types for any properties and methods that exist in the class. Often, we may need to change these types for different situations, which is what generics are used for. Without generics, we would need to repeat our code multiple times, each time with a different type. Using generics, we can make a single class definition with a placeholder, that the user can specify at the time of declaring the object.
Consider the following node declaration, which declares the data as an integer.
If we want to store data in this node, we need to make sure the data is an integer value, otherwise, it will give a type error. Using this node, we can implement a stack that can hold integer values.
The way this is currently defined, if we want to create a stack of Strings, we would need to create a new node and stack object with appropriate typing. This is inconvenient to do, since it makes our codebase larger, and in turn, less maintainable. An alternative to doing this would be to make the node and stack classes generic. Let's start with the node class, and see how we can make it generic.
When we define the node class, we add a <T> to the definition. This tells Java that there will be a placeholder, T, which will be provided by the user. When the user provides the T value, every T in the code is replaced by the type that the user provides. From here, we just replace every instance of int with T, allowing for the user to specify any of the int types as required. When we turn to our stack class, we do a similar set of changes
We again change every instance of int into a T. When we declare the node that holds the top value, we declare it as Node<T>, allowing for the type to be set for the generic node object as well. Now, if a user wants to declare a stack of type String, they would simply write Stack<String> test = new Stack<String>();
This declaration would cause all of the T types to be replace with String. If a user wants to declare a second stack of type integer, then just replace <String> with <Integer>, and they are done. The main power of generics is that they allow us to prevent code repetition. This means that instead of needing two separate objects, which need to be tested separately, we have a single object that we can test easier, and maintain easier.