Usage of Java generics
Usage of Java generics

Usage of Java generics

Java Arrays vs. Java Collection Framework

Java Generics is a facility that was introduced by Sun starting with Java 5. The introduction of this technology in development was received happily by some programmers (especially those who used the concept of template functions and classes in C++) and was criticized by others for the new grade of complexity that it brings to programming.

The main advantages brought by generics are better readability of the written code (it’s much easier for a programmer to efficiently use a class / method that he didn’t write, or to understand a specific piece of code) and, most importantly, compile-time check of the objects that are inserted into arrays.

In our examples, we will keep it simple with the classes Animal, Dog and Bird. Of course the classes Dog and Bird extend the class Animal. We will also have an interface Flying that will be implemented by the Bird class.

The content of these classes are irrelevant or the discussion at hand. Due to polymorphism, an Array or a Collection of animals can hold an anonymous animal, a bird, a dog.

Thus there is no compile error / runtime error while running this code:

Animal[] animals = new Animal[3];
Animal an = new Animal();
Dog d = new Dog();
Bird b = new Bird();
animals[0] = an;
animals[1] = d;
animals[2] = b;
ArrayList < Animal > animals2 = new ArrayList < Animal > ();
animals2.add(an);
animals2.add(d);
animals2.add(b);
for (Animal a: animals) {
    a.eats();
}
for (Animal a: animals2) {
    a.eats();
}

What if we want to store in our animals[] array a dogs[] array? 

This code will successfully compile and run:

Dog[] dogs = new Dog[2];
dogs[0] = new Dog();
dogs[1] = new Dog();
animals = dogs;
for (Animal a: animals) {
a.eats();
}

What happens if, instead of Arrays we have a Collection?

The following code gives a compilation error:

ArrayList birds = new ArrayList();
birds.add(new Bird());
birds.add(new Bird());
animals2 = birds;

The NetBeans Java compiler gives us the following error message:

incompatible types found: java.util.ArrayList required: java.util.ArrayList animals2 = birds;

We receive the same error when we try to assign a JButton array reference to an animal array reference:

JTextField[] texte = new JTextField[1];
animals = texte;

Obviously, it’s correct for this compile-time error message to appear when we try to use a Animal Array reference to hold an Array reference of a class that doesn’t extend the Animal class. This strange problem is caused by the fact that a new, generic-containing, Java code must be compatible with old code (prior to Java 5) that doesn’t contain generics.

This compatibility was reached by making generics available only in development / compile time. This behaviour is called “type erasure”.

Java documentation: https://download.oracle.com/javase/tutorial/java/generics/erasure.html

“When a generic type is instantiated, the compiler translates those types by a technique called “type erasure”— a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics” 

When the java program is executed, the JVM changes ArrayList<Something> into a simple ArrayList reference.

Let’s consider the next example:

ArrayList birds = new ArrayList();
birds.add(new Bird());
birds.add(new Bird());
addAnimal(birds);
public static void addAnimal(ArrayList a) {
    a.add(new Dog());
}

If we wouldn’t have had our compile-time error on the method addAnimal(birds) the program would have inserted a new Dog into our Birds Collection, which certainly would have been very wrong.

One other thing: if the same code would have been rewritten using arrays, the program would have passed compilation but on runtime we would have received a nasty ArrayStoreException runtime exception.

This is the difference between Arrays and Collections using Generics: because of type erasure, Collections cannot have an exception like ArrayStoreException because, at runtime, we cannot know the type of the elements in the collection (all ArrayList<E> are ArrayList<> at runtime).