Over a million developers have joined DZone.

An Introduction to Generics in Java – Part 4

· Java Zone

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

This is a continuation of an introductory discussion on generics, previous parts of which can be found here.

Before jumping into subtyping in generics, I thought it will be good if I talk a little about generic methods and automatic type inference.

Generic Methods

Just like generic class, we can define generic methods in Java.  A generic method is a method which defines its own type parameters. In this case, the type parameter’s scope is limited to the method where it’s declared. To declare a generic method, we just list the type parameters before its return type, like this –

public class MyClass {
    public static <T> void myFirstGenericMethod(T param) {

We can invoke this generic method in the following way –

MyClass. <String>myFirstGenericMethod("Hello World");

If this was an instance method, then we would have called it like this –

myInstance. <String>myFirstGenericMethod("Hello World");

Notice how we have passed the type argument while calling the method. Generally, the rule is as follows –

class/instance_var. <type_argument_list>methodName(argument list…..);

Our above method call will also compile without specifying the type parameters explicitly. This happens because of type inference – the java compiler’s ability to automatically determine the type argument during generic method call/generic instance creation. We will talk more about this in a few moments.

Let’s write another useful generic method which will add all elements of an array into a list –

public class Utils {
    public static <T> void addAll(List<T> myList, T[] arr) {
        for (T elem: arr) {

The above code should be self-explanatory at this point.

Type Inference

Type inference is Java compiler’s ability to look at generic method invocation and instance creation to determine the type arguments that make the code work. This algorithm is capable of determining the type of the arguments and the type that the result is being assigned or returned, provided that there is enough information for the inference algorithm to do so.

According to the official Oracle Java tutorial –

The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.

There are two situations where type inference algorithm comes into play –

  1. When an object of a generic type is being created
  2. When a generic method is being invoked

Type Inference during generic instance creation

Generally, type inference for instance creation works like this – first, the compiler tries to deduce the type arguments from the constructor arguments in the object creation expression (the one with the new). If it fails to deduce the type arguments for any of the type parameters from there, it then uses information from the context in which the creation expression appears.

In one of my previous posts, I have demonstrated a short-cut way to create generic instances without specifying the type argument –

MyPrettySimpleGenericClass<Integer> item = new

Here, we see that the instance that is being created is being assigned to a variable, which is of typeMyPrettySimpleGenericClass<Integer>. This line will be valid only if the generic instance being created also has theInteger as its type argument (no, any subtype/supertype of Integer will also not do, we’ll see why in a future post). When type inference algorithm tries to deduce the type argument that will be applicable for this instance creation expression, it first takes a look at the constructor arguments. Since the above code doesn’t have any, it then takes a look at the left hand side of the assignment operator and sees that only Integer can make the statement valid. Thus it automatically passes Integer as the type argument to the generic instance being created.

If constructor arguments are available and if the inference algorithm can determine the applicable type arguments from them, then it completely ignores the assignment context. Let’s consider an example –

List<Number> list = new ArrayList<>(Arrays.asList(0L, 0L));

We are passing long values to the asList method, which takes an arbitrary number of arguments as its input, and returns an object of type List containing those elements. As a result, the inference algorithm determines its return type to beList<Long>, and returns an instance of this type. This returned value is then again used by the inference algorithm to determine the type argument of the ArrayList being created, which is finally set to Long as well. Since List<Long> andList<Number> are totally incompatible type (again, more on this in a future post), the compiler generates an error (although in Java 8 it compiles fine).

What happens if there are no constructor arguments, or assignment of the generic instance being created? Let’s consider an example of that too –

Iterator<String> it = new ArrayList<>().iterator();

In the above example, the object creation expression occurs inside a chain of method calls. As the type inference for object creation expression is only allowed for an assignment context (this is only true for JDK 7 as JDK 8 has improved the inference algorithm a bit by including the method invocation context), the compiler is unable to determine the correct type. As a result, a compile-time error is issued by the compiler.

Similar reasoning can be applied for the following blocks of code –

class TestDrive {
static void printAll(List<Integer> list) {
for (Integer i: list) {
public static void main(String[] args) {
printAll(new ArrayList<>());  // Error in Java 7

In these cases, the recommended approach is to introduce a variable and assign the object instance to it first. It is then used in the expression replacing the object creation expression.

Type inference generally fails for anonymous inner classes. Consider another example –

Comparator<Integer> cmp = new Comparator<>() {
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);

In the above example, the type inference cannot determine the applicable type arguments from the context, and will throw a compilation error.

Type Inference during generic method invocation

Type inference algorithm for generic methods works in the same way as the instance creation. Here, type inference first observes the method arguments to determine the type parameter, and then observes the context in which it is being used.

If we recall our myFirstGenericMethod method, we can see that the type inference is capable of determining the appropriate type argument by observing the string argument. Same thing is true for our addAll utility method.

What happens if the method doesn’t have any arguments which correspond to its type parameter? In this case the compiler tries to determine the type from the method invocation context. Let’s write a method which will create lists for us if we specify the initial capacity –

static <T> List<T> createList(int capacity) {
List<T> list = new ArrayList<>(capacity);
return list;

We can call it in the following way –

List<Integer> list = createList(10);

Our utility method doesn’t have any arguments which correspond to the type parameter. Hence inference algorithm cannot deduce anything by observing the arguments in the method invocation. It then tries to observe the calling context and sees that the result returned by the method is being assigned to a reference of type List<Integer>, and determines the type argument to the method to be Integer.

What if the above method is not being called from an assignment context? The compiler cannot determine the type argument! If we call our previous printAll method like this –


then the compiler will throw an error (JDK 8 can determine the type argument). We have to specify the type argument explicitly in this case, or introduce a variable to hold the value returned by the first method, and then pass it as argument to the latter.

We can declare generic methods in a generic class declaration too. What happens if the type parameter of the generic method matches with the type parameter of the generic class? I leave this to the reader to experiment!

Lastly, the Java Specification says that we can only specify type arguments when we call generic methods using dot notation. As a result, calling our createList method like this will result in a compile-time error (obviously from inside the class where it’s defined) –

<Integer>createList(5);  // Error, won’t compile

In this case, if the generic method is a static one, we use class name to invoke the method. If it’s an instance method, we use an instance reference (or this super) to call it.

That’s it for today. Stay tuned for the next post!


  1. Java Generics and Collections by Naftalin, Philip Wadler
  2. Angelika Langer: Java Generics Faqs

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.


Published at DZone with permission of MD Sayem Ahmed, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}