Skip to content

Generics

Generics let you write classes and methods that work with a type you specify later, so the compiler can check types and you avoid casting.

Stage 1advanced1 min read

What are generics?

Imagine a list that holds anything. You put a String in, but it comes back as an Object, and you must convert it (cast) yourself. If you guess wrong, the program fails while running. Generics fix this. You tell a class, interface, or method which type to use, and the compiler checks every put and get.

You write the container or the algorithm once and leave a blank for the type. You fill the blank later. You see this most in collections (lists, maps, stacks): List<String>, Map<String, EmailTemplate>, Stack<EditorMemento>.

In simple terms:

  • List = “a list of something.”
  • List<String> = “a list of String only.” The compiler knows every element is a String, and won’t let you add an Integer.

Why use them?

  • Type safety: The compiler stops a wrong type from going into the list or map.
  • No casting: You don’t need (String) list.get(0). The compiler already knows it is a String.
  • Clear intent: Map<String, EmailTemplate> tells the reader “keys are strings, values are email templates.”

Example

// List of strings only
List<String> drinks = new ArrayList<>();
drinks.add("Cola");
drinks.add("Water");
String first = drinks.get(0);  // No cast needed

// Map: key = String, value = EmailTemplate
Map<String, EmailTemplate> templates = new HashMap<>();
templates.put("welcome", new WelcomeEmail());
EmailTemplate t = templates.get("welcome");  // Type is EmailTemplate

// Stack of mementos
Stack<EditorMemento> history = new Stack<>();
history.push(new EditorMemento("content"));
EditorMemento m = history.pop();

Syntax in a class

public class Registry<T> {
    private final Map<String, T> map = new HashMap<>();

    public void put(String key, T value) {
        map.put(key, value);
    }

    public T get(String key) {
        return map.get(key);
    }
}
// Use: Registry<EmailTemplate> reg = new Registry<>();
  • T is the “type parameter”: a stand-in name for a type. When you write Registry<EmailTemplate>, T becomes EmailTemplate everywhere in that object. So put takes an EmailTemplate value and get returns an EmailTemplate.

Where you see it in this repo

  • Builder: List<String> drinks in the burger meal. The builder uses List<String> too.
  • Prototype: Map<String, EmailTemplate> templates in EmailTemplateRegistry.
  • Observer: List<Subscriber> subscribers in YouTubeChannel.
  • Memento: Stack<EditorMemento> history in Caretaker.

In this topic

Done with this topic?

How well do you know it?

PreviousMethod overriding and polymorphism