Generics were added to the Java language within J2SE 5.0, and there was much rejoicing. It was finally possible to deal with containers in a type-safe manner. Prior to the availability of generics, Java developers had to do things like this:
List people = new ArrayList(); people.add(new Person("Donkey Kong")); people.add(new Person("Guybrush Threepwood")); Person pirate = (Person) people.get(1);
This kind of code is very fragile since it is not easy to keep track what is inside a container. If at runtime, the object you retrieve is not of the type that you’re expecting, you can get a ClassCastException. It is also remarkably easy to pollute a container by shoving objects of different types inside there, which makes it even more difficult to keep track of the types of the objects inside. Workarounds included littering code with instanceof checks, or creating a wrapper class (for example a class called PeopleList that would delegate to an internal List instance) around the container so that you could have control over the types of objects being inserted.
When generics finally arrived, people were ecstatic because now you could do things like this:
List<Person> people = new ArrayList<Person>(); people.add(new Person("Donkey Kong")); people.add(new Person("Guybrush Threepwood")); Person pirate = people.get(1); //It just works!
This meant no-more ugly workarounds, which means that things are awesome! Right?