skip to content

Generic Classes and Interfaces

Declaring a class or interface with type parameters and using them in fields, parameters and return types. Interviewers usually ask you to write a small typed container to see whether the syntax is second nature.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is a generic class in Java, and how do you declare one with a type parameter?

level: juniorimportance: must knowfreq 85%

answer

  1. Type parameter in <> after the class name: class Box<T>
  2. T usable in fields, params, return types
  3. Type argument supplied at use: Box<String> (parameterized type)
  4. Diamond <> infers the argument
  5. Erasure: one class at runtime, T becomes its bound

basics

~20 s

A generic class is a class that takes a type as a parameter, written in angle brackets after the class name, like class Box<T>. You then use T as a placeholder type inside the class for fields and methods. When you create an object you supply a real type, e.g. Box<String>.

solid answer

~40 s

A generic class is a class parameterized over one or more types. You declare it by adding a type-parameter section in angle brackets right after the class name: class Box<T>. Inside the class, T behaves like a real type you can use for fields (private T value), method parameters (void set(T v)), and return types (T get()). The single uppercase letter is just a conventional name. When you use the class you create a parameterized type by supplying a concrete type argument, Box<String>, which makes the compiler treat every T as String for that instance, giving you compile-time type safety and removing casts. The same idea applies to interfaces: interface Container<T> { T get(); }. Generics replaced the old approach of using Object plus manual casts.

go deeper

for a junior

Can declare class Box<T>, use T for a field and a get/set method, and instantiate Box<String> with the diamond operator.

for a middle

Explains the type parameter vs type argument distinction, multiple parameters (Pair<K,V>), generic interfaces, and that raw types lose safety.

for a senior

Connects generics to compile-time safety vs ClassCastException, explains type erasure and its consequences (one runtime class, T erased to its bound), and the static-member restriction.

for a principal

Frames generics as part of API design — when to make a class generic vs not, naming conventions, interaction with erasure for library evolution, and the cost of leaking raw types into a public API.

## What problem do generics solve? Before generics (Java 5, 2004), a reusable container like a list stored elements as `Object`. You could put anything in, but taking something out required a manual cast, and the compiler could not stop you from putting the wrong thing in: ```java List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); // cast needed list.add(42); // compiles, but is a logic bug ``` A **generic class** lets you tell the compiler *what type of thing* a class works with, so the compiler enforces it and inserts casts for you. ## Terminology, defined - **Type parameter**: a placeholder name (e.g. `T`) declared by the class for a type that will be supplied later. It lives in angle brackets `<...>` right after the class name. - **Type argument**: the *actual* type you supply when you use the class, e.g. `String` in `Box<String>`. - **Parameterized type**: the result of supplying type arguments, e.g. `Box<String>`. This is the concrete type you actually use in code. - **Generic type / raw type**: `Box` (no arguments) is the *raw type*; using it loses type safety and the compiler warns. ## Declaring a generic class ```java public class Box<T> { // T is the type parameter private T value; // field uses T public void set(T value) { // method parameter uses T this.value = value; } public T get() { // return type uses T return value; } } ``` The `<T>` after `Box` introduces `T` as a name usable anywhere inside the class body where a type is expected. By convention type parameters are single uppercase letters: `T` (type), `E` (element), `K`/`V` (key/value), `N` (number), `R` (result). These are just names; you could write `<Element>` if you wanted. ## Generic interfaces Interfaces work identically: ```java public interface Container<T> { T get(); void put(T item); } ``` A class implementing it either fixes the type (`class StringBox implements Container<String>`) or stays generic (`class GenericBox<T> implements Container<T>`). ## Instantiating a parameterized type ```java Box<String> b = new Box<>(); // diamond operator <> infers String on the right b.set("hi"); String s = b.get(); // no cast: compiler knows it is String b.set(42); // COMPILE ERROR: 42 is not a String ``` The **diamond operator** `<>` (Java 7+) lets you omit the type argument on the right-hand side because the compiler infers it from the variable's declared type. ## What the compiler actually does (type erasure, briefly) Generics are a *compile-time* feature. After the compiler checks your types and inserts casts, it **erases** the type parameters: at runtime `Box<String>` and `Box<Integer>` are both just `Box`, and `T` becomes its bound (here, `Object`). This is called **type erasure**. It means there is exactly one `Box` class at runtime regardless of how many type arguments you used, and you cannot ask an object at runtime which type argument it was created with. ## Multiple type parameters A class can take several type parameters, separated by commas: ```java public class Pair<K, V> { private final K key; private final V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } Pair<String, Integer> p = new Pair<>("age", 30); ``` ## Why it matters Generics give you **compile-time type safety** (mistakes caught while compiling, not as runtime `ClassCastException`s), **eliminated casts** (cleaner code), and **reusable, single implementations** that work for many types. Almost the entire Java Collections Framework is built on generic classes and interfaces.

  • Why are the angle brackets on the left of a declaration a 'type parameter' but on the right of a use a 'type argument'?
    On the left (class Box<T>) you are *introducing* a placeholder name the class is parameterized over — that is the parameter. On the right (Box<String>) you are *supplying* the concrete value for that placeholder — that is the argument. Same parameter-vs-argument distinction as a method's formal parameter vs the value you pass.
  • Can a generic class have static fields of type T?
    No. Type parameters belong to an *instance* (each Box<X> would want its own T), but static members are shared across all instances and all parameterizations, so there is no single T to mean. The compiler rejects static T fields and static methods cannot use the class's type parameter (a static method can declare its own).

saying these in an interview costs you the question

  • Confusing the type parameter (declared, e.g. T) with the type argument (supplied, e.g. String)
  • Thinking each Box<X> is a separate class at runtime — erasure means there is one Box
  • Saying you must repeat the type on the right: new Box<String>() instead of new Box<>()
  • Believing generics add runtime overhead or runtime type checks — they are compile-time only

context

open as a page

How do you declare and use a generic class with multiple type parameters, and how does it differ from a generic interface like Map<K,V>?

level: middleimportance: should knowfreq 62%

basics

~20 s

List several type parameters in the angle brackets separated by commas, like class Pair<K, V>. Each one is an independent placeholder you can use for fields, parameters, and return types. A generic interface such as Map<K,V> is the same idea but for an interface — implementing classes supply or pass along those types.

open as a page

What is the difference between a raw type, a parameterized type, and Box<Object>, and why does using a raw type defeat the purpose of generics?

level: middleimportance: should knowfreq 55%

basics

~20 s

A raw type is using a generic class with no type argument, like Box. A parameterized type supplies one, like Box<String>. A raw Box turns off generic type checking and forces casts, bringing back the runtime errors generics were meant to prevent, so the compiler warns against it. Box<Object> still keeps type checking on.

open as a page

Given type erasure, why can't a generic class have a static field of its type parameter or use that parameter in a static method?

level: seniorimportance: should knowfreq 48%

basics

~20 s

A type parameter belongs to an instance — each object can have a different actual type. Static members are shared by all instances and all parameterizations at once, so there is no single type the parameter could mean. The compiler therefore forbids static fields or static methods that use the class's type parameter.

open as a page

When designing a public API, how do you decide whether a class should be generic, and what are the long-term consequences of that choice under type erasure?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Make a class generic when it genuinely works with a caller-chosen type that should flow through its API without casts, like a container. Avoid it when the type is fixed or only one method needs it. Because Java erases generics, once a type is public it is hard to change later without breaking callers.

open as a page