In this first part of a series of Java Generics tutorials, we’ll look
at basic usage of generics with collection framework. This is the most
common use case of generics. In later parts of the tutorial, you’ll
learn advanced topics like how to create your own generic classes in
Java.
What is Generics
Generics is a powerful mechanism which results in cleaner code which
is robust and easy to understand. Generics allows you to bind classes
and objects to certain types. The most common use of generics is with
collection classes like ArrayList, HashMap etc.
If you’ve learned Java programming after Java version 5 or 6 then you
might already be using generics and don’t know about it. If you’ve
written this type of code then you are already using generics,
ArrayList<String> userNames = new ArrayList<String>();
The
<String>
part is generics syntax. If you are not familiar with this syntax then we’ll see what it means and what advantages it provides.If you are not familiar with collections framework in Java, its a set of Interfaces and Classes in the Java API. These classes provide you the ability to store multiple values like arrays but unlike arrays, the size of collections can change if more elements are added to them. Collection classes also provide you the ability to store key-value pairs.
Type Safe Collections
Using generics provides you type safety while using collections. Type
safety in very simple terms is avoiding runtime exceptions by finding
errors at compile time.
Let’s see an example of what the problem is and how generics solves it. Here is a simple piece of code that creates a
List
adds some elements to the list and then retrieves the elements from the list to get their total.
When you run this code you’ll get a nice looking stack trace of a
ClassCastException
as output.
The second element in the List is a String so when our code tries to
type cast it to Integer, then JVM throws this exception. Generics
provides you the power to ensure this doesn’t happen. The compiler will
flag such a problem to you instead of an exception at runtime. Here’s
how the code will look when generics is included.
List<Integer> numbers = new ArrayList<Integer>(); //typed list
numbers.add(10);
number.add("20"); //compile time error
int total = 0;
for(int i = 0; i < numbers.size(); i++) {
Integer number = numbers.get(i); //no cast needed
total += number.intValue();
}
This time the compiler knows that the
numbers
list can only contain elements of Integer type. So when you try to add a
String to this list the compiler will report that error. As you might
have noticed, you don’t need a type-cast when you get an element from
the list at line no. 6. This is because again the compiler knows that
elements in numbers
List are of type Integer so the type-cast is automatically done by the compiler.
With an untyped List, you can add any type of elements to the list,
and the compiler won’t complain about it. With generics the compiler
would only let you add only objects of the type which you used while
creating the list. Lets see a simple example,
List untypedList = new ArrayList();
//any object can be added to untypedList
untypedList.add("Hello World");
untypedList.add(123);
List<String> stringList = new ArrayList<String>();
//only String objects can be added to stringList
stringList.add("Hello World");
stringList.add(123); //compile time error, cannot add Integer to String List
The above code demonstrates that with untyped lists, the programmer
has to ensure he/she is adding the right type of objects to a List. If a
wrong element gets added to the List, you can get a
ClassCastException
while retrieving elements from the List. But with generics, the
compiler takes care of that. Like at line no. 8 we tried to add an Integer
object to a List which takes only String
objects. The compiler would disallow this making your life easier.
Using generics syntax makes your code more readable and avoids
accidental addition of wrong objects into a collection. Like lets say
you were writing a piece of code which took a List of
Employee
class objects and prepared two lists of employee names (String
objects) and ids (Long
objects). Here’s the code without generics,
class Employee {
private String name;
private Long id;
public String getName() {
return name;
}
public Long getId() {
return id;
}
//name and id setters
}
..
public void manageEmployees(List employees) {
List empNames = new ArrayList(); //raw list
List empIds = new ArrayList(); //raw list
for(int i = 0; i < employees.size(); i++) {
Employee employee = (Employee)employees;
empNames.add(employee.getName());
empIds.add(employee.getName()); //did I do something wrong here?
}
//do something with empNames and empIds lists
}
private String name;
private Long id;
public String getName() {
return name;
}
public Long getId() {
return id;
}
//name and id setters
}
..
public void manageEmployees(List employees) {
List empNames = new ArrayList(); //raw list
List empIds = new ArrayList(); //raw list
for(int i = 0; i < employees.size(); i++) {
Employee employee = (Employee)employees;
empNames.add(employee.getName());
empIds.add(employee.getName()); //did I do something wrong here?
}
//do something with empNames and empIds lists
}
All is fine with this code except that I accidentally added employee
names to employee ID list. So I’ll get an exception at runtime when I
try to use the information stored in the
empIds
list as I’ll be expecting Long
object but I’ll get a String
object. If I were using generics, the compiler would have caught this. Lets see a generic version of this code,public void manageEmployees(List<Employee> employees) {
List<String> empNames = new ArrayList<String>(); //List of Strings
List<Long> empIds = new ArrayList<Long>(); //List of Longs
for(int i = 0; i < employees.size(); i++) {
Employee employee = employees;
empNames.add(employee.getName());
empIds.add(employee.getName()); //compile time error
}
//do something with these two lists
}
Comments
Post a Comment