"Hi, Amigo!"

"Hi, Ellie!"

"Today, Rishi and I are going to tell you all about generics."

"Wait, I think I already know almost everything."

"Almost everything, but not everything."

"Really? OK, I'm ready to listen."

"Then let's begin."

"In Java, generics are classes that have type parameters."

"As for why generics were invented, see the comments in the code:"

Example
ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

How to solve the problem using Generics:

Example
ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

"This code simply won't compile, and the error caused by adding the wrong data type will be noticed during compilation."

"Yes, I already know this."

"Okay, good. Repetition is the mother of learning."

"But Java's creators were a little lazy when they created generics. Instead of making full-fledged types with parameters, they slipped in a slick optimization. In reality, they didn't add any information about type parameters to generics. Instead, all the magic occurs during compilation."

Code with generics
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
What really happens
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

"That is slick."

"Yes, but this approach has a side effect. No information about type parameters is stored inside a generic class. This approach later became known as type erasure."

"In other words, if you have your own class with type parameters, you cannot use information about them inside the class."

Code with generics
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
What really happens
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

"During compilation, all parameter types are replaced with Object. And inside the class there is no information about the type passed to it."

"Yes, I agree, that's not the best."

"It's not that scary. I'll tell you later how to get around this problem."

But there's more. Java lets you specify a parent type for type parameters. The extends keyword is used for this. For example:

Code with generics
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
What really happens
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

"Note two facts:"

"First, you can't pass just any type as a parameter — you can only pass a Cat or a class that inherits Cat."

"Second, inside the Zoo class, variables of type T can now call the Cat class's methods. The column on the right explains why (because Cat will be substituted everywhere there is a T)"

"Yes. If we say that Cat or a subclass of Cat is passed as the type argument, then we are certain that type T will always have the Cat class's methods."

"Well, that's clever."