codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Tutorials
☕

Java Programming

57 / 65 topics
56Java Wrapper Classes57Java Generics58Java RegEx59Java Threads60Java Lambda
Tutorials/Java Programming/Java Generics
☕Java Programming

Java Generics

Updated 2026-05-12
30 min read

Java Generics

Generics in Java provide a way to create classes, interfaces, and methods that operate on objects of various types while providing type safety. This feature is crucial for writing flexible and reusable code. In this tutorial, we will explore generic classes, generic methods, bounded type parameters, and wildcards.

Introduction

Java generics allow you to define classes, interfaces, and methods that can work with objects of different types without compromising type safety. This is particularly useful in collections where you want to store a list of elements of any specific type. Generics help prevent runtime errors related to type mismatches and improve code readability and maintainability.

Generic Classes

A generic class is defined by using angle brackets (<>) after the class name. The type parameter represents the type that will be used when an object of the class is created.

Example: A Simple Generic Class

Java
1// Box.java
2public class Box<T> {
3 private T content;
4
5 public void setContent(T content) {
6 this.content = content;
7 };
8
9 public T getContent() {
10 return content;
11 }
12}

In this example, T is a type parameter that represents the type of the object stored in the Box. You can create instances of Box for different types.

Example: Using the Generic Class

Java
1// Main.java
2public class Main {
3 public static void main(String[] args) {
4 Box<String> stringBox = new Box<>();
5 stringBox.setContent("Hello");
6 System.out.println(stringBox.getContent());
7
8 Box<Integer> integerBox = new Box<>();
9 integerBox.setContent(123);
10 System.out.println(integerBox.getContent());
11 }
12}

Output

Output
Hello
123

In this example, we create a Box for String and Integer types. The type parameter T is replaced with String and Integer, respectively.

Generic Methods

A generic method can be defined within a non-generic class or within a generic class. It allows you to specify the type of the parameters and return type at the time of method invocation.

Example: A Generic Method

Java
1// Util.java
2public class Util {
3 public static <T> void printArray(T[] array) {
4 for (T element : array) {
5 System.out.println(element);
6 }
7 }
8}

In this example, printArray is a generic method that takes an array of any type and prints its elements.

Example: Using the Generic Method

Java
1// Main.java
2public class Main {
3 public static void main(String[] args) {
4 String[] strings = {"Apple", "Banana", "Cherry"};
5 Util.printArray(strings);
6
7 Integer[] integers = {1, 2, 3};
8 Util.printArray(integers);
9 }
10}

Output

Output
Apple
Banana
Cherry
1
2
3

In this example, we use the printArray method to print arrays of String and Integer types.

Bounded Type Parameters

Bounded type parameters allow you to restrict the types that can be used as type arguments in a generic class or method. This is done by using an extends keyword followed by a type bound.

Example: A Generic Class with Bounded Type Parameters

Java
1// Box.java
2public class Box<T extends Number> {
3 private T content;
4
5 public void setContent(T content) {
6 this.content = content;
7 }
8
9 public T getContent() {
10 return content;
11 }
12}

In this example, the type parameter T is bounded to Number, meaning it can only be a subclass of Number.

Example: Using the Bounded Type Parameter

Java
1// Main.java
2public class Main {
3 public static void main(String[] args) {
4 Box<Integer> integerBox = new Box<>();
5 integerBox.setContent(123);
6 System.out.println(integerBox.getContent());
7
8 Box<Double> doubleBox = new Box<>();
9 doubleBox.setContent(45.67);
10 System.out.println(doubleBox.getContent());
11 }
12}

Output

Output
123
45.67

In this example, we create a Box for Integer and Double types, which are both subclasses of Number.

Wildcards

Wildcards in generics allow you to write more flexible code that can work with different types without specifying the exact type. There are three types of wildcards:

  1. Unbounded wildcard (?): Represents any type.
  2. Upper bounded wildcard (<? extends T>): Represents a type that is a subclass of T.
  3. Lower bounded wildcard (<? super T>): Represents a type that is a superclass of T.

Example: Using Unbounded Wildcards

Java
1// Util.java
2public class Util {
3 public static void printList(List<?> list) {
4 for (Object element : list) {
5 System.out.println(element);
6 }
7 }
8}

In this example, the method printList accepts a list of any type.

Example: Using Upper Bounded Wildcards

Java
1// Util.java
2public class Util {
3 public static void printNumberList(List<? extends Number> list) {
4 for (Number element : list) {
5 System.out.println(element);
6 }
7 }
8}

In this example, the method printNumberList accepts a list of any type that is a subclass of Number.

Example: Using Lower Bounded Wildcards

Java
1// Util.java
2public class Util {
3 public static void addNumbers(List<? super Integer> list) {
4 list.add(10);
5 list.add(20);
6 }
7}

In this example, the method addNumbers accepts a list of any type that is a superclass of Integer.

Practical Example

Let's create a practical example that combines all the concepts we have learned. We will create a generic class for a stack data structure and demonstrate its usage with different types.

Stack.java

Java
1// Stack.java
2public class Stack<T> {
3 private List<T> elements = new ArrayList<>();
4
5 public void push(T element) {
6 elements.add(element);
7 }
8
9 public T pop() {
10 if (elements.isEmpty()) {
11 throw new IllegalStateException("Stack is empty");
12 }
13 return elements.remove(elements.size() - 1);
14 }
15
16 public boolean isEmpty() {
17 return elements.isEmpty();
18 }
19}

Main.java

Java
1// Main.java
2public class Main {
3 public static void main(String[] args) {
4 Stack<String> stringStack = new Stack<>();
5 stringStack.push("Hello");
6 stringStack.push("World");
7
8 while (!stringStack.isEmpty()) {
9 System.out.println(stringStack.pop());
10 }
11
12 Stack<Integer> integerStack = new Stack<>();
13 integerStack.push(10);
14 integerStack.push(20);
15
16 while (!integerStack.isEmpty()) {
17 System.out.println(integerStack.pop());
18 }
19 }
20}

Output

Output
World
Hello
20
10

In this example, we create a Stack for String and Integer types. The stack operations work seamlessly with different types.

Summary

ConceptDescription
Generic ClassesDefine classes that can operate on objects of various types without compromising type safety.
Generic MethodsDefine methods that can take parameters and return types of any specified type at the time of invocation.
Bounded Type ParametersRestrict the types that can be used as type arguments in a generic class or method.
WildcardsAllow writing more flexible code that can work with different types without specifying the exact type.

What's Next?

Now that you have a solid understanding of Java generics, you are ready to explore regular expressions in Java. Regular expressions provide powerful tools for pattern matching and text manipulation. Continue your journey by learning about Java RegEx.


PreviousJava Wrapper ClassesNext Java RegEx

Recommended Gear

Java Wrapper ClassesJava RegEx