7-day free trial — no payment today.

Generics in Java

Write reusable, type-safe code with generic classes and methods, bounded type parameters, wildcards (? extends, ? super), and type erasure.

Generic Classes

A generic class uses a type parameter (like `<T>`) in its declaration. When you create an instance, you provide the actual type. This lets you write one class that works correctly with `String`, `Integer`, `User`, or any other type — without casting or losing type safety.

1. Basic Generic Class

Declare a class with `<T>` and use `T` as the field/return type. The compiler enforces types at compile time.

2. Multiple Type Parameters

A class can have more than one type parameter. Common convention: `T` for type, `K` for key, `V` for value.

3. Why Generics Beat Object

Before generics, developers used `Object` as a universal type. This required unsafe casting. Generics eliminate that problem.

Generic Methods

A generic method declares its own type parameter right before the return type: `public <T> T method(T arg)`. This is independent of any class-level type parameter. Generic methods are useful for utility/helper functions that should work across many types.

1. Basic Generic Method

Declare `<T>` before the return type. The method infers `T` from the argument you pass.

2. Generic Method with Return Type

The type parameter can also be the return type. This is common for factory methods and utility wrappers.

3. Generic Utility — Swap

Generic methods are great for common algorithms like swap, min, max that should work for any type.

Bounded Type Parameters

Sometimes you want a generic that only accepts certain types. Java lets you set an upper bound with `<T extends SomeClass>` — meaning `T` must be `SomeClass` or a subclass. You can also bound by an interface. This lets you call methods from that class/interface inside your generic code.

1. Upper Bound with extends

`<T extends Number>` means T can only be Number or its subclasses (Integer, Double, Long, etc.).

2. Bound by Interface

`<T extends Comparable<T>>` means T must implement the Comparable interface, so you can call compareTo() inside the method.

3. Multiple Bounds

A type parameter can extend one class AND implement multiple interfaces using `&`.

Wildcards

Wildcards (`?`) represent an unknown type in a generic context. There are three forms: unbounded (`?`), upper bounded (`? extends Type`), and lower bounded (`? super Type`). They are used in method parameters — not in class declarations. Understanding when to use each is one of the more nuanced parts of Java generics.

1. Unbounded Wildcard — ?

`List<?>` means a list of some unknown type. You can read from it as Object, but you cannot add to it (except null).

2. Upper Bounded Wildcard — ? extends Type

`List<? extends Number>` accepts a list of Number or any subtype. Use this when you want to READ values from a collection.

3. Lower Bounded Wildcard — ? super Type

`List<? super Integer>` accepts a list of Integer or any supertype. Use this when you want to WRITE (add) values into a collection.

Type Erasure

Java generics are a compile-time feature only. The compiler checks types, but once it generates bytecode, all type parameters are erased and replaced with `Object` (or the bound type). At runtime, a `List<String>` and a `List<Integer>` are both just `List`. This is called type erasure, and it has important implications for what you can and cannot do with generics.

1. What Type Erasure Does

After compilation, `T` becomes `Object`. Casts are inserted automatically by the compiler wherever needed.

2. Cannot Create Generic Arrays

Because of type erasure, you cannot write `new T[]`. The workaround is to accept a typed array from the caller or use a List instead.

3. Cannot Use instanceof with Generic Types

Since type info is erased at runtime, `instanceof List<String>` is illegal. You can only check the raw type.

Generic Interfaces

Just like classes, interfaces can be generic. You define the type parameter on the interface, and implementing classes either fix the type (`implements Repository<User>`) or remain generic (`implements Repository<T>`). This is the foundation of how Java collections, Comparable, Iterable, and many framework patterns work.

1. Defining and Implementing a Generic Interface

Declare `interface MyInterface<T>` and implement it with a concrete type like `implements MyInterface<String>`.

2. Generic Repository Pattern

A classic real-world use: define a generic `Repository<T>` interface that provides common CRUD operations for any entity type.

3. Implementation That Stays Generic

Instead of fixing the type, the implementing class can stay generic too by passing the type parameter through.