Generics let us abstract the type of the object. Generics were introduced in java 1.5.
Agenda for this article:
- Generics Overview
- Generic Methods
- Bounds
- Wildcards
Generics Overview
Let’s have an example
List list = new ArrayList();
list.add("apple");
list.add("kiwi");
In the above example, we used List without using generics.
When type is not specified in <>, the default type of the list is Object
It’s equivalent to List<Object>
Now let’s see with generics
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
new ArrayList<String>() and new ArrayList<>() are equivalent. Compiler can find out the type by context and the second one just shorter version. This future available from Java 1.7
<String> will define what data type our list should hold. So why do we need generics if it works without them just fine?
We want to write our code with fewer bugs. By using generics we can control what data type can be used by our list and if we use different data types we will get a compiler error.
Let’s see one more example without generics
- We can see that it allowed StringBuilder to get into our list. Because its list of Object and StringBuilder is a child of the object so it’s totally fine for a compiler.
- And during runtime when we get back items from the list trying to convert to String, we get our exception.
Now, let’s see an example with generics
- We are getting compiler errors when we try to add StringBuilder to our list. The error during compilation is always better than during runtime.
Ok, so we used generics where it was already created for us. Now let’s create something by ourselves.
Bucket<E>
we mention in the declaration of the class that we use generics and then inside our class we can use it as we have this type.- The actual type of
<E>
will be assigned by the client code. - In the main method take a look how our
Bucket
class can take String and then Integer.
Let’s clarify some definitions:
Bucket<E>
when we use generics like this we refer to it as type parameter
Bucket<String>
and when we actually pass the type for our generic we refer to it as type argument.
The most commonly used type parameter names are:
E — Element
K — Key
N — Number
T — Type
V — Value
S, U, V etc. — second, third, and fourth types
One more example
<E, S, T>
that’s how we use multiple generics type parameters.- Interfaces can utilize generics as well.
Generic Methods
It’s a similar concept to generic classes and interfaces. We can have methods with their own type parameters that have a scope limited to the method where they are declared.
<E, S> boolean
We need to specify type parameters before method return type and then we can use them as method argumentsisSame(E element, S secondElement)
Bounds
What if we want to use generics but we don’t want the client code to pass any object type. The bounds help us to restrict the types that can be passed as type arguments.
- We use
extends
to specify outbounds. Every class, including the bound class itself, that extends/implements our bound class can be used as a type argument for generics.
Why not just have private Car car;
in the Garage class instead of having private T car;
. It’s actually the same reason here — fewer defects. It is better to have a compilation error than a runtime exception.
Garage g = new Garage();
g.putInside(new BMW());
Audi audi = (Audi)g.getOut(); // runtime exception// ------Garage<Audi> g1 = new Garage<>();
g1.putInside(new BMW()); // does not compile here
Wildcards
We can use wildcards to lose the restrictions of generics. ?
means every type.
Upper Bounded Wildcard
Let’s say we want to create a method that can accept List<Number>
, List<Integer>
, List<Double>
we can use as the argument type of our method List<? extends Number>
Integer
andDouble
both extendNumber
so we can pass a list of them safely in our method.- Upper bounded let us use an unknown type(?) or a subtype of it.
Lower Bounded Wildcard
It is opposed to upper bounded. Let’s say we have this structure
Audi -> Car -> Object
----------------------public static void printList(List<? super Audi> list) {
// code here..
}
printList
method now can acceptList<Audi>, List<Car>, List<Object>
Unbounded Wildcard
Let’s say we want to create a method that is able to print out the list of any object.
public static void printList(List<Object> list) {
for (Object el : list) {
System.out.println(el);
}
}
Is it the correct implementation? No. For example, it will not work with List<String>
, actually, it will work with List<Object>
only.
Now, let’s see the correct implementation
public static void printList(List<?> list) {
for (Object el : list) {
System.out.println(el);
}
}
- Now we can pass List with any type
Summary
Generics let us abstract the type of the object. By using generics we avoid runtime exceptions and catch issues with types during the compile time. There is a concept called Type Erasure that actually replaces all type parameters in generic types with their bounds or Object type.
Thank you for reading.
The resources used in this article:
1. Generics documentation from Oracle
Please take my Java Course for video lectures.This article is part of the series of articles to learn Java programming language from Tech Lead Academy:Introduction to programming
OS, File, and File System
Working with terminal
Welcome to Java Programming Language
Variables and Primitives in Java
Convert String to numeric data type
Input from the terminal in Java
Methods with Java
Java Math Operators and special operators
Conditional branching in Java
Switch statement in Java
Ternary operator in Java
Enum in Java
String class and its methods in Java
Loops in Java
Access modifiers in Java
Static keyword in Java
The final keyword in Java
Class and Object in Java
Object-Oriented Programming in Java
OOP: Encapsulation in Java
OOP: Inheritance in Java
OOP: Abstraction in Java
OOP: Polymorphism in Java
The method Overriding vs Overloading in Java
Array in Java
Data Structures with Java
Collection framework in Java
ArrayList in Java
Set in Java
Map in Java
Date and Time in Java
Exception in Java
How to work with files in Java
Design Patterns
Generics in Java
Multithreading in java
Annotations in Java
Reflection in Java
Reflection & Annotations - The Powerful Combination
Run terminal commands from Java
Lambda in Java
Unit Testing in Java
Big O Notation for coding interviews
Top Java coding interview questions for SDET