Apart from Classes and Interfaces, individual methods can also define their own generic type parameters!

This gives methods the same expressive generic type safety system that Classes and Interfaces have!

public class MyClass {
  //             ↓ This is the methods' generic type parameter
  public static <T> T SomeFunction(T a) {
    //... Some implementation
  }
}

Examples of Generic Methods

Example 1: This example doesn’t really make sense but it’s to show the syntax

In this class we have:

  • Class Generic Type Parameter: T
  • Method Generic Type Parameters: S, U
public class MyClass<T> {
  public <S> S SomeFunc1(T a){/*...*/} // Method generic return type
  public <S> T SomeFunc2(S a, S b){/*...*/} // Method generic paramter type
  public <S, U> T SomeFunc3(S a, U b){/*...*/} // Multiple method generic paramter types
  public <T> T SomeFunc4(T a){/*...*/} // wow what? does T in this method belong to the method or the class???
}
 
public static void main(String[] args) {
  // T -> Integer
  MyClass<Integer> x = new MyClass<>();
 
  // SomeFunc1's signature effectively becomes -> public Number SomeFunc1(Integer a)
  // 123 is autoboxed into an Integer type
  // S -> Number
  Number he = x.<Number>SomeFunc1(123);
 
  // SomeFunc1's signature effectively becomes -> public Date SomeFunc1(Integer a)
  Date hehe = x.<Date>SomeFunc1(123);
  Date hehehe = x.SomeFunc1(123); // This is equivilant to the line above! Java compiler is able to infer the type
 
  // SomeFunc2's signature effectively becomes -> public String SomeFunc2(Date a, Date b)
  String huhu = x.<Date>SomeFunc2(new Date(), new Date());
 
  // SomeFunc3's signature effectively becomes -> public String SomeFunc3(Integer a, Double b)
  String har = x.<Integer, Double>SomeFunc3(123, 1.1d);
 
  // SomeFunc4's signature effectively becomes -> public String SomeFunc4(String a)
  // In this case, since both the Class and this method defines a generic 'T', the method 'overrides' the Class
  // 'T' in this method is then completely seprate from the 'T' in the Class
  String oop = x.<String>SomeFunc4("oop");
}

Example 2: An actual example from the Java library

The Java Collections interface defines the following method public static <T> void sort(List<T> list, Comparator<? super T> c)

Java offers a Comparator interface to allow you to implement custom comparing algorithms.

  public interface Comparator<T> {
    int compare(T o1, T o2);
    //...other stuff...
  }

The T Ensures that the type of the List you pass into the Collections.sort() method matches the type of the Comparator you passed into the function.

Let’s test this type safety with the following code:

import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Collections;
 
class Main {
	static class StrComparator implements Comparator<String> {
		@Override
		public int compare(String o1, String o2) {
			return o1.length() - o2.length();
		}
	}
	public static void main(String[] args) {
		List<Integer> intList = new ArrayList<>();
		intList.add(1);
		intList.add(2);
		intList.add(3);
		
		Collections.sort(intList, new StrComparator());
	}
}

Compilation Error

error:
(inference variable T#2 has incompatible bounds equality constraints: Integer upper bounds: String,Object)

error: compilation failed

We see that the Java compiler complains that the types are incompatible! Success! This is an example of how generic types help to enforce compile time type safety.

Example 3: Another interesting use of method generics in the java library

This one is a little complicated so it gets its own note: java-generic-methods-collection-toarray

What’s Next?

java-generic-bounded-type-parameters