Generics

Generics make code safer and more flexible. This lesson covers Java generics usage.

What are Generics

Generics are parameterized types that allow classes and methods to work with multiple types.

Benefits of Generics

Benefit Description
Type safety Compile-time type checking
Code reuse One code works with multiple types
Eliminate casting No manual type conversion needed

Generic Classes

Syntax

JAVA
public class ClassName<T> {
    // Use T as type
}

Example: Generic Class

JAVA
public class Box<T> {
    private T value;
    
    public void set(T value) {
        this.value = value;
    }
    
    public T get() {
        return value;
    }
    
    public static void main(String[] args) {
        // Store Integer
        Box<Integer> intBox = new Box<>();
        intBox.set(100);
        int num = intBox.get();  // No casting needed
        
        // Store String
        Box<String> strBox = new Box<>();
        strBox.set("Hello");
        String str = strBox.get();
        
        System.out.println("Integer Box: " + num);
        System.out.println("String Box: " + str);
    }
}
▶ Try it Yourself

Multiple Generic Parameters

JAVA
public class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
    
    @Override
    public String toString() {
        return key + "=" + value;
    }
    
    public static void main(String[] args) {
        Pair<String, Integer> pair = new Pair<>("Alice", 95);
        System.out.println(pair);  // Alice=95
    }
}

Generic Methods

Syntax

JAVA
public static <T> methodName(parameters) {
    // Use T
}

Example: Generic Methods

JAVA
public class GenericMethod {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    
    public static <T> T getFirst(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        return array[0];
    }
    
    public static <T extends Comparable<T>> T getMax(T[] array) {
        T max = array[0];
        for (T element : array) {
            if (element.compareTo(max) > 0) {
                max = element;
            }
        }
        return max;
    }
    
    public static void main(String[] args) {
        Integer[] nums = {1, 2, 3, 4, 5};
        String[] names = {"Alice", "Bob", "Charlie"};
        
        printArray(nums);    // 1 2 3 4 5
        printArray(names);   // Alice Bob Charlie
        
        System.out.println("First number: " + getFirst(nums));  // 1
        System.out.println("First name: " + getFirst(names));  // Alice
        
        System.out.println("Max: " + getMax(nums));  // 5
        System.out.println("Max name: " + getMax(names));  // Charlie
    }
}
▶ Try it Yourself

Generic Interfaces

Defining Generic Interface

JAVA
public interface Repository<T> {
    void add(T item);
    T get(int index);
    List<T> getAll();
}

Implementing Generic Interface

JAVA
public class ArrayListRepository<T> implements Repository<T> {
    private List<T> list = new ArrayList<>();
    
    @Override
    public void add(T item) {
        list.add(item);
    }
    
    @Override
    public T get(int index) {
        return list.get(index);
    }
    
    @Override
    public List<T> getAll() {
        return new ArrayList<>(list);
    }
    
    public static void main(String[] args) {
        Repository<String> repo = new ArrayListRepository<>();
        repo.add("Alice");
        repo.add("Bob");
        System.out.println(repo.getAll());  // [Alice, Bob]
    }
}

Generic Wildcards

Unbounded Wildcard ?

JAVA
public static void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

Upper Bounded Wildcard ? extends T

JAVA
// Only accepts Number and its subclasses
public static double sum(List<? extends Number> list) {
    double total = 0;
    for (Number num : list) {
        total += num.doubleValue();
    }
    return total;
}

Lower Bounded Wildcard ? super T

JAVA
// Only accepts Integer and its superclasses
public static void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

Example: Wildcards

JAVA
import java.util.ArrayList;
import java.util.List;

public class WildcardDemo {
    // Unbounded wildcard
    public static void printList(List<?> list) {
        for (Object item : list) {
            System.out.print(item + " ");
        }
        System.out.println();
    }
    
    // Upper bounded wildcard
    public static double sum(List<? extends Number> list) {
        double total = 0;
        for (Number num : list) {
            total += num.doubleValue();
        }
        return total;
    }
    
    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        ints.add(1);
        ints.add(2);
        ints.add(3);
        
        List<String> strs = new ArrayList<>();
        strs.add("Alice");
        strs.add("Bob");
        
        printList(ints);  // 1 2 3
        printList(strs);  // Alice Bob
        
        System.out.println("sum: " + sum(ints));  // 6.0
    }
}
▶ Try it Yourself

Type Erasure

Java generics are implemented through type erasure—generic information is erased after compilation.

JAVA
// Before compilation
List<String> list = new ArrayList<>();
list.add("Hello");

// After compilation (type erasure)
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0);  // Automatic casting
⚠️ Note: Generic type information cannot be obtained at runtime. List<String> and List<Integer> are the same type at runtime.

Generic Constraints

Example: Bounded Generics

JAVA
// T must implement Comparable
public static <T extends Comparable<T>> T min(T a, T b) {
    return a.compareTo(b) <= 0 ? a : b;
}

// T must be a subclass of Number
public static <T extends Number> double doubleValue(T num) {
    return num.doubleValue();
}
▶ Try it Yourself

Common Generic Names

Name Meaning
T Type
E Element
K Key
V Value
N Number

❓ Frequently Asked Questions

Q Why use generics?
A Type safety, code reuse, and eliminating casting.
Q Do generics support primitive types?
A No, use wrapper classes. For example, List<int> should be List<Integer>.
Q What's the difference between <?> and <Object>?
A <?> is unknown type—can only read, not write. <Object> is Object type—can read and write.

📖 Summary

📝 Exercises

  1. Generic container: Implement a generic Stack class
  2. Generic method: Implement a generic method to convert an array to a List
  3. Bounded generics: Implement a method to compare two objects

Next Lesson

In the next lesson, we'll learn about Practice: Collection Applications — applying collection knowledge.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏