Generics in Java: Why They Are Needed and How to Use Them

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Generics in Java: Why They Are Needed and How to Use Them



Generics are one of the key features of the Java language, introduced in version J2SE 5.0. They allow you to create classes, interfaces, and methods that work with different data types while maintaining strong typing at compile time. In this article, we will look at why generics are needed, how to use them correctly, and what errors they help avoid.



Why Are Generics Needed in Java?



Before the advent of generics, programmers used type casting and working with Object. This led to two main problems:



  • Lack of type safety — an object of any type could be added to a collection, and the error would only appear at runtime.
  • Need for explicit casting — when retrieving an element from a collection, a cast was required, which cluttered the code.


// Without Generics (Java before 5)List list = new ArrayList();list.add("string");list.add(123); // No error at compile timeString s = (String) list.get(0); // Must cast explicitly


// With GenericsList<String> list = new ArrayList<>();list.add("string");// list.add(123); // Compilation error!String s = list.get(0); // No cast needed


Main advantages of Generics:

  • Type safety — type errors are detected at compile time.
  • Elimination of explicit casts — code becomes cleaner and more readable.
  • Code reusability — one implementation works with different types.


Creating Generic Classes and Methods



Generic Class



To create a class that works with an arbitrary type, simply specify the type parameter in angle brackets after the class name:



public class Box<T> {    private T content;

public void setContent(T content) { this.content = content; }

public T getContent() { return content; }

public static void main(String[] args) { Box<String> stringBox = new Box<>(); stringBox.setContent("Hello, Java!"); System.out.println(stringBox.getContent());

Box<Integer> intBox = new Box<>(); intBox.setContent(42); System.out.println(intBox.getContent()); }}


In this example, T is the type parameter. When creating a Box<String> object, all occurrences of T are replaced with String.



Generic Method



Methods can also be parameterized. The type parameter is specified before the return type:



public class Util {    public static <T> void printArray(T[] array) {        for (T element : array) {            System.out.print(element + " ");        }        System.out.println();    }

public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; String[] strArray = {"A", "B", "C"};

printArray(intArray); // 1 2 3 4 5 printArray(strArray); // A B C }}


Note: the type parameter <T> comes before void. This is mandatory for generic methods.



Bounded Type Parameters



Sometimes you need to restrict which types can be used as a parameter. The keywords extends and super are used for this.



Upper Bound (extends)



Restricts the type from above — only the specified class itself or its subclasses can be used:



public class Calculator<T extends Number> {    private T value;

public Calculator(T value) { this.value = value; }

public double square() { return va

Blogs

Book Recommendations