Java Generics: Generic Class and Method Examples for Beginners

Java Generics: Generic Class and Method Examples for Beginners


Welcome to our beginner-friendly guide on Java generic class and method examples for beginners. If you’re a student or new Java learner, this page will show you what generics are, why they matter, and how to write generic classes and generic methods step by step. We’ll use simple examples you can copy, run, and learn from. For more Java basics and topics, check our Java section at coddesire.com/java/.

What are Java Generics and why use them?

Java generic class and method examples for beginners – generic class visual
How a generic class enforces type safety with different type arguments

Java generics let you write reusable, type-safe code. Instead of repeating the same class or method for different data types, you can parameterize code with a type variable (like T or E). This gives you:

  • Type safety at compile time (fewer ClassCastException errors)
  • Cleaner code without manual casts
  • Reusable libraries and utilities (like Java Collections)

Important to know: Java implements generics using “type erasure.” That means your type arguments (like String or Integer) are not available at runtime. You also cannot use primitive types (like int) as type arguments; use wrapper types (Integer) instead.

Key takeaways: Java generic class and method examples for beginners
  • Generics add compile-time type safety and reduce casts.
  • Use wrapper types (Integer, Double) instead of primitives.
  • Erasure: type arguments are not available at runtime.
  • Prefer parameterized types (List<String>) over raw types (List).
  • Design flexible APIs with bounds and wildcards (PECS rule).

What is a generic class in Java? (Simple example)

Java generic class and method examples for beginners – generic method visual with bounds
Generic method with a bounded type compares numbers and returns the maximum

A generic class declares one or more type parameters in angle brackets. This answers the common question, “what is a generic class in Java?” Here is a minimal example you can run:

Code
class Box<T> {
    class="cd-keyword cd-access">private T value;

    class="cd-keyword cd-access">public void set(T value) {
        this.value = value;
    }

    class="cd-keyword cd-access">public T get() {
        return value;
    }
}

class="cd-keyword cd-access">public class BoxDemo {
    class="cd-keyword cd-access">public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        intBox.set(42);

        Box<String> strBox = new Box<>();
        strBox.set("Hello, Generics");

        System.out.println("intBox: " + intBox.get());
        System.out.println("strBox: " + strBox.get());
    }
}
Output
intBox: 42
strBox: Hello, Generics

In this Java generics tutorial for beginners, notice how we used the same class for Integer and String without casting.

How to create a generic class in Java step by step

  1. Choose a clear, descriptive class name (for example, Box).
  2. Add a type parameter in angle brackets (for example, <T> stands for “type”).
  3. Use the type parameter wherever you need a type inside the class (fields, method parameters, return types).
  4. Create objects with a concrete type (for example, new Box<String>()). Use the diamond operator <> on the right side.
  5. Compile and run to see type safety in action—no unchecked casts.

Generic methods in Java explained (with examples)

A method is “generic” when it declares its own type parameter before the return type. This is different from a generic class because a generic method can live in a non-generic class and still be generic.

Code
class Utils {
    class="cd-keyword cd-access">public static <T> T identity(T x) {
        return x;
    }

    class="cd-keyword cd-access">public static <T> void printArray(T[] arr) {
        for (T e : arr) {
            System.out.print(e + " ");
        }
        System.out.println();
    }
}

class="cd-keyword cd-access">public class GenericMethodDemo {
    class="cd-keyword cd-access">public static void main(String[] args) {
        Integer i = Utils.identity(10);      // T inferred as Integer
        String s = Utils.identity("Java");   // T inferred as String

        System.out.println(i);
        System.out.println(s);

        String[] words = {"A", "B", "C"};
        Utils.printArray(words);
    }
}
Output
10
Java
A B C

This is a beginner guide to Java generic methods with examples: the compiler often infers the type from the arguments, so you don’t need to write Utils.<Integer>identity(10) unless inference fails.

Type parameters in Java generics

Common type parameter names:

  • T – Type
  • E – Element (often used in collections)
  • K, V – Key, Value (often used in maps)
  • R – Result/Return type

You can define multiple type parameters and even add bounds to restrict allowed types.

Multiple type parameters

Code
class Pair<K, V> {
    class="cd-keyword cd-access">private final K key;
    class="cd-keyword cd-access">private final V value;

    class="cd-keyword cd-access">public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    class="cd-keyword cd-access">public K getKey() { return key; }
    class="cd-keyword cd-access">public V getValue() { return value; }
}

Upper bounds with extends

Use an upper bound to restrict T to extend a class or implement interfaces. Multiple bounds are allowed (class first, then interfaces):

Code
class MathUtils {
    class="cd-keyword cd-access">public static <T extends Number & Comparable<T>> T max(T a, T b) {
        return (a.compareTo(b) >= 0) ? a : b;
    }
}

Wildcards and PECS (Producer Extends, Consumer Super)

Wildcards add flexibility to your APIs:

  • Use ? extends T when a parameter produces values of type T (you only read from it).
  • Use ? super T when a parameter consumes values of type T (you add/put T into it).
Code
class="cd-package">import java.util.List;

class WildcardDemo {
    static double sum(List<? extends Number> nums) {
        double total = 0.0;
        for (Number n : nums) {
            total += n.doubleValue();
        }
        return total;
    }

    static void addDefaults(List<? super Integer> dest) {
        dest.add(1);
        dest.add(2);
    }
}
PECS decision flow (choosing between T, ? extends, ? super)
Start: What will the parameter do?
↓
Only read/iterate elements produced by the source? Use ? extends T (Producer Extends)
↓
Only add/put values of T into the destination? Use ? super T (Consumer Super)
↓
Need to both read and write the same specific type? Use exact T (e.g., List<T>)
↓
Need subtype-specific methods or multiple bounds? Use bounded type params, e.g., <T extends Number & Comparable<T>>

Type inference and the diamond operator

Since Java 7, you can use the diamond operator <> to let the compiler infer constructor type arguments. Java also infers method type arguments in many cases.

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class InferenceDemo {
    class="cd-keyword cd-access">public static void main(String[] args) {
        List<String> names = new ArrayList<>(); // <> infers <String>
        names.add("Ana");
        names.add("Beck");
        System.out.println(names);
    }
}

Difference between a generic class and a generic method

Aspect Generic Class Generic Method
Definition Class declares type parameters (for example, class Box<T>) Method declares its own type parameters (for example, <T> T id(T x))
Scope of type parameters Available to all non-static members of the class Available only inside the method where declared
Usage Reusable data structures and domain models Reusable utilities and algorithms
Instantiation You specify type arguments at object creation Type arguments usually inferred from method arguments

Common mistakes and important limitations

  • Do not use raw types like List; always use List<T> to keep type safety.
  • You cannot use primitive types as type arguments (use Integer, Double, etc., not int or double).
  • You cannot create arrays of parameterized types (no new List<String>[10]).
  • You cannot use instanceof with parameterized types (no obj instanceof List<String>).
  • Type parameters are not allowed in static contexts of a generic class (static T value is not valid).
  • Remember erasure: type arguments are not available at runtime.
Practical checklist: designing generics for beginners
Generic class
  • Pick clear type parameter names (T, E, K, V).
  • Add bounds if behavior requires it (e.g., T extends Number).
  • Avoid static members using type parameters.
  • Do not create arrays of parameterized types.
  • Test with multiple concrete types (e.g., String and Integer).
Generic method
  • Declare type params before return type: <T> T method(T x).
  • Rely on inference; specify <T> explicitly only when needed.
  • Use wildcards to widen APIs (PECS: extends for read, super for write).
  • Document constraints and whether the method reads/writes.
  • Unit test with various types and edge cases (nulls, empties).

Practice: Java generics example program for students

This complete program combines a generic class, generic methods, bounds, and wildcards. It’s a practical “Java generics example program for students.”

Code
class="cd-package">import java.util.ArrayList;
class="cd-package">import java.util.Arrays;
class="cd-package">import java.util.List;

class="cd-keyword cd-access">public class GenericsDemo {

    // Generic class
    static class Box<T> {
        class="cd-keyword cd-access">private T value;

        Box() { }

        Box(T value) {
            this.value = value;
        }

        void set(T value) {
            this.value = value;
        }

        T get() {
            return value;
        }

        class="cd-annotation">@Override
        class="cd-keyword cd-access">public String toString() {
            return String.valueOf(value);
        }
    }

    // Generic class with two type parameters
    static class Pair<K, V> {
        class="cd-keyword cd-access">private final K key;
        class="cd-keyword cd-access">private final V value;

        Pair(K key, V value) {
            this.key = key;
            this.value = value;
        }

        K getKey() { return key; }
        V getValue() { return value; }
    }

    // Utility methods
    static class Utils {
        // Generic method
        static <T> T identity(T x) {
            return x;
        }

        // Generic method using the same type for list elements
        static <T> void swap(List<T> list, int i, int j) {
            T temp = list.get(i);
            list.set(i, list.get(j));
            list.set(j, temp);
        }

        // Bounded type parameter with multiple bounds
        static <T extends Number & Comparable<T>> T max(T a, T b) {
            return (a.compareTo(b) >= 0) ? a : b;
        }

        // Wildcards: producer extends
        static double sum(List<? extends Number> nums) {
            double s = 0.0;
            for (Number n : nums) {
                s += n.doubleValue();
            }
            return s;
        }

        // Wildcards: consumer super
        static void addSomeIntegers(List<? super Integer> list) {
            list.add(1);
            list.add(2);
        }
    }

    class="cd-keyword cd-access">public static void main(String[] args) {
        Box<Integer> intBox = new Box<>(100);
        Box<String> strBox = new Box<>("Generics");
        System.out.println("intBox: " + intBox.get());
        System.out.println("strBox: " + strBox.get());

        Integer id = Utils.identity(7);
        String word = Utils.identity("hello");
        System.out.println("identity: " + id + ", " + word);

        Integer mi = Utils.max(12, 9);
        Double md = Utils.max(3.14, 2.71);
        System.out.println("max: " + mi + " and " + md);

        List<Integer> ints = new ArrayList<>();
        Utils.addSomeIntegers(ints);
        ints.add(10);
        System.out.println("sum ints: " + Utils.sum(ints));

        List<Double> doubles = Arrays.asList(1.5, 2.5, 4.0);
        System.out.println("sum doubles: " + Utils.sum(doubles));

        List<String> names = new ArrayList<>();
        names.add("Ava");
        names.add("Ben");
        names.add("Cia");
        Utils.swap(names, 0, 2);
        System.out.println("swapped names: " + names);

        Pair<String, Integer> p = new Pair<>("score", 95);
        System.out.println("pair: " + p.getKey() + "=" + p.getValue());
    }
}
Output
intBox: 100
strBox: Generics
identity: 7, hello
max: 12 and 3.14
sum ints: 13.0
sum doubles: 8.0
swapped names: [Cia, Ben, Ava]
pair: score=95

Why should beginners use generics in Java?

  • They reduce runtime errors by catching type mismatches at compile time.
  • They keep your code DRY and reusable (write once, use for many types).
  • They make your intent clear (for example, List<String> instead of List).

Trend watch

OpenJDK’s Project Valhalla is exploring performance-focused features like specialized generics for primitive/value types. This work is ongoing and not yet part of standard Java, but it’s a direction to watch as you progress beyond beginner level.

FAQ: Java generics explained with easy code samples

What is a generic class in Java with a simple example?

A class that declares a type parameter, like Box<T>. See the Box example above.

How do I write a generic method in Java?

Declare a type parameter before the return type, for example: <T> T identity(T x). The compiler usually infers T from the argument.

Why should beginners use generics in Java?

They provide type safety and reduce boilerplate casts, helping you avoid common runtime errors early.

What is the difference between a generic class and a generic method?

A generic class defines type parameters at the class level, while a generic method defines them at the method level. See the comparison table above.

How do type parameters work in Java generics?

Type parameters act like placeholders for actual types. They’re checked at compile time, but erased at runtime. You can constrain them with bounds (extends, super) and use multiple bounds.

Next steps

Keep practicing by converting your non-generic utilities and data structures into generic versions. Then explore collections, streams, and functional interfaces, which rely heavily on generics. For more beginner-friendly topics, visit our Java index: https://coddesire.com/java/.

Sources / Further reading

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted