# Understanding Java Generics<T>

> Before we dive into the specifics, it's crucial to understand the basics. The term "Generics" means parameterized types, which enable classes, interfaces, and methods to be created with placeholders for the types they handle.

To understand generics more clearly, let's consider a simple class hierarchy in the animal kingdom. We have a base class called `Animal` and two subclasses, `Bird` and `Dog`.

```java
public class Animal {
    public void eat() {
        System.out.println("Animal is eating...");
    }
}

public class Bird extends Animal {
    public void fly() {
        System.out.println("Bird is flying...");
    }
}

public class Dog extends Animal {
    public void bark() {
        System.out.println("Dog is barking...");
    }
}
```

Now, let's create a `Cage` generic class that can hold any type of `Animal`:

```java
public class Cage<T extends Animal> {
    private T animal;

    public void add(T animal) {
        this.animal = animal;
    }

    public T release() {
        return animal;
    }
}
```

The `Cage` class is a generic class, and it uses the type parameter `T` which extends `Animal`. This implies that the `Cage` class can only hold an `Animal` or any of its subclasses.

The type parameter above &lt;T&gt; can actually be anything &lt;WhateverYouWant&gt;.. But it is standardized to the letter "T" meaning -&gt; Type.

## **Wildcard Bounds**

There are times when we may not know the exact type to use with a class or method. Wildcards can be quite handy in such situations. Wildcards are represented by the question mark symbol `?`.

### **Upper Bounded Wildcards**

Upper-bounded wildcards restrict the unknown type to be a specific type or a subtype of that. They're expressed using the `extends` keyword.

Let's create a method that will accept a `Cage` of `Animal` or any subclass of `Animal`:

```java
public void feedAnimals(Cage<? extends Animal> cage) {
    Animal animal = cage.release();
    animal.eat();
}
```

### **Lower Bounded Wildcards**

Lower bounded wildcards restrict the unknown type to be a specific type or a superclass of that. They're expressed using the `super` keyword.

For instance, let's consider a method that can add any `Animal` or superclass of `Animal` into a `Cage`:

```java
public void addToCage(Cage<? super Animal> cage, Animal animal) {
    cage.add(animal);
}
```

### **Unbounded Wildcards**

In cases where the functionality is independent of the type argument, we can use unbounded wildcards. They're expressed using the question mark symbol `?`.

```java
public void printCageContent(Cage<?> cage) {
    System.out.println(cage.release());
}
```

### Conclusion

Generics in Java add stability to your code by allowing compile-time type checks and reducing the risk of ClassCastException that was common while working with collections. Through examples from the animal kingdom, we've seen how we can use generics to create more robust and type-safe code. Whether you're creating a `Cage` for `Birds` or `Dogs`, or feeding animals from an unknown `Cage`, generics make your life as a Java engineer a little easier.
