🌐 العربية
11 Lesson 3 of 5

Generic Methods

One Method for All Types

Write a single method that works with Integer, String, Student, or any type

<T> void Return Types printArray No Duplicates

1 The Duplication Problem

In Lesson 1, we saw the challenge: if we want a method to print an Integer array, then a String array, then a Student array, we need three separate methods -- identical logic, duplicated code. Let's look at what that actually looks like.

Three overloaded printArray methods
public static void printArray(Integer[] array) { for (Integer element : array) System.out.println(element); } public static void printArray(String[] array) { for (String element : array) System.out.println(element); } public static void printArray(Student[] array) { for (Student element : array) System.out.println(element); }
DRY Violation
Same logic, copy-pasted three times. What if we need 100 types? This violates the DRY principle (Don't Repeat Yourself). Every time we add a new type, we have to write yet another identical method. There has to be a better way.

2 The Object Approach (And Its Limitations)

Your first instinct might be: "Everything in Java extends Object, so why not just use Object[]?" Good thinking! Let's try it.

2a Printing with Object — It Works!

Using Object[] for printing
public static void printArray(Object[] array) { for (Object element : array) System.out.println(element); }

This works perfectly for printing! Since every class has a toString() method inherited from Object, println can handle anything. But what about returning elements?

2b Returning with Object — The Problem

A method that returns an element
public static Object getFirst(Object[] array) { return array[0]; } // Usage -- casting required! Integer[] nums = {10, 20, 30}; Integer first = (Integer) getFirst(nums); // Must cast! String[] names = {"Ali", "Sara"}; String name = (String) getFirst(names); // Must cast! // DANGER: Wrong cast compiles fine! Integer wrong = (Integer) getFirst(names); // RUNTIME ERROR!
Casting is Dangerous
When the method returns Object, we must cast the result back to the specific type. And if we cast to the wrong type? The code compiles without any warnings, but crashes at runtime with a ClassCastException. The compiler cannot protect us here.

3 The Generic Method Solution

Here is the big idea: instead of Object, we use a type parameter. The key syntax rule is that <T> goes before the return type. Let's see how the duplicated printArray method becomes a single generic method.

The generic printArray method
public static <T> void printArray(T[] array) { for (T element : array) System.out.println(element); }
Understanding the Syntax
  • <T> -- Declares this is a generic method with type parameter T
  • void -- The return type (this method returns nothing)
  • T[] -- The parameter type; the array can be of any type
  • The compiler figures out what T is from the argument you pass

Usage Calling the Generic Method

One method, many types
Integer[] nums = {10, 20, 30}; String[] names = {"Ali", "Sara", "Ahmad"}; Double[] prices = {9.99, 19.99, 29.99}; printArray(nums); // T is Integer printArray(names); // T is String printArray(prices); // T is Double
Output
10 20 30 Ali Sara Ahmad 9.99 19.99 29.99
One Method Handles All Types
ONE method handles ALL types! No duplication, no casting, complete type safety. The compiler infers the type automatically from the argument you pass.

4 Generic Methods with Return Types

Generic methods are not limited to void. They can also return a value of the generic type. This is where they really shine compared to the Object approach -- no casting required!

A generic method that returns a value
public static <T> T getFirst(T[] array) { return array[0]; }
Reading the Signature

Let's read <T> T getFirst(T[] array) piece by piece:

  • <T> -- "This method has a type parameter called T"
  • T (after <T>) -- "The return type is T -- whatever T turns out to be"
  • T[] array -- "The parameter is an array of T"

So if you pass an Integer[], T becomes Integer, and the return type becomes Integer.

Usage No Casting Needed

Using the generic getFirst method
Integer[] nums = {10, 20, 30}; Integer first = getFirst(nums); // Returns Integer -- no cast! String[] names = {"Ali", "Sara"}; String name = getFirst(names); // Returns String -- no cast!
Type-Safe Returns
No casting needed for the return value! The compiler knows that if you pass Integer[], the return type is Integer. If you accidentally try to assign the result to the wrong type, you get a compile-time error instead of a runtime crash.

5 Side-by-Side Comparison

Let's put the Object approach and the Generic approach next to each other so you can clearly see the difference.

BEFORE: Using Object
public static Object getFirst(Object[] arr) { return arr[0]; } // Must cast -- error-prone! Integer n = (Integer) getFirst(nums);
AFTER: Using Generics
public static <T> T getFirst(T[] arr) { return arr[0]; } // No cast needed -- type-safe! Integer n = getFirst(nums);
The Key Difference
With Object, the compiler forgets the original type -- you have to remember it yourself and cast manually. With generics, the compiler tracks the type throughout, so it knows exactly what goes in and what comes out.

6 Complete Example

Let's put it all together in a complete, runnable program. You can copy this code into your IDE and run it right away to see generic methods in action.

GenericMethodDemo.java
public class GenericMethodDemo { // Generic print method public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } // Generic method with return type public static <T> T getFirst(T[] array) { return array[0]; } public static void main(String[] args) { Integer[] nums = {10, 20, 30}; String[] names = {"Ali", "Sara", "Ahmad"}; System.out.println("Numbers:"); printArray(nums); System.out.println("Names:"); printArray(names); Integer firstNum = getFirst(nums); String firstName = getFirst(names); System.out.println("First number: " + firstNum); System.out.println("First name: " + firstName); } }
Output
Numbers: 10 20 30 Names: Ali Sara Ahmad First number: 10 First name: Ali

7 Key Takeaways

What You Learned in This Lesson
  1. Generic methods declare <T> before the return type -- this is the syntax that makes a method generic.
  2. The compiler infers the type from the arguments -- you do not need to specify the type explicitly when calling the method.
  3. One method replaces many type-specific methods -- no more copy-pasting the same logic for different types.
  4. Return types can be generic too (<T> T) -- the return type matches whatever type you pass in.
  5. No casting needed -- the compiler handles type tracking automatically, catching errors at compile time instead of runtime.
  6. Generic methods can exist in both generic and non-generic classes -- you do not need a generic class to write a generic method.
Coming Up Next
In Lesson 4, we will explore Generic Interfaces -- how to define interfaces with type parameters and implement them in concrete classes. This is the next step toward mastering Java generics.
← Lesson 2: Generic Classes Lesson 4: Generic Interfaces →
Code Block Theme