Method Generics in Java

Method Generics are great for making Utility and Factory classes simpler to read and easier to use.

Generics are fun in Java; at least some people find them fascinating. Others try to avoid them at all costs from their evil and sadistic nature – there doesn’t seem to be any middle ground.

Generics don’t bother me, as long as they’re used at the correct time and for the right reasons. Like anything, they’re a tool, and any tool is good for the right job.

Many only learn Class Generics in Java. These are great, but sometimes they make code look ugly. Let’s see this in action.

Say we want to pair objects together using a factory. Here’s how we could store the objects together:

public class Pair <K, V> {

   private final K key;
   private final V value;

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

   public K getKey() {
      return key;
   }

   public V getValue() {
      return value;
   }
}

Now the factory can use generics to create them:

public class SampleFactory <K, V> {

   public Pair<K, V> createNewPair(K key, V value){
      return new Pair<>(key, value);
   }

   public static void main(String[] cheese){
      Pair<Integer, String> pair = new SampleFactory<Integer, String>().createNewPair(1, "hello");
   }
}

While the factory is overly-simple, it highlights what I often come across with Generics. Here, SampleFactory is taking two generics at the class level, giving it to the scope of every method within the class. That’s why createNewPair knows what types of arguments to pass back when it’s generating a new Pair object.

But look at that line to create a new Pair instance through the factory. SampleFactory is stateless, so why do we need to call “new” in front of it? That keyword actually creates instances of SimpleFactory on the heap with each call. It would be better have createNewPair be a static call, but generics from a class don’t get carried to static methods because the generics are tied to the class’s instance.

Also note the empty parenthesis after specifying the expected types to SampleFactory. Experienced Java developers will know that this is implicitly calling the default constructor, but this can be confusing for some people coming from other languages. Why are we calling something with no arguments on a class that’s stateless? That’s annoying.

There’s just a lot of extra syntax before the meat of the call, createNewPair, which by itself is straight forward.

What’s the solution? Method-based generics.

They’re little known but perfect for something like this. Here’s the updated SampleFactory class:

public class SampleFactory {

   public static <K, V> Pair<K, V> createNewPair(K key, V value){
      return new Pair<>(key, value);
   }

   public static void main(String[] cheese){
      Pair<Integer, String> pair = SampleFactory.createNewPair(1, "hello");
   }
}

Wow! Looks weird doesn’t it? For those using Java for a while, it probably even feels dirty. But look at the resulting cleanliness! See how the generic declaration was moved to the function level, allowing it to be called statically? This makes the createNewPair call concise and easier to read, and it moves the scope of the generic to just that method. Now there’s no need to create empty objects on the heap every time a new Pair object is generated!

Even better, the call to SampleFactory is simpler as we don’t have to specify the generic types to it anymore. “new SampleFactory<Integer, String> ()” becomes “SampleFactory.”

Method generics are great for utility classes – stateless classes that aim to help or mutate data in a functional way or to provide useful methods to a variety of other classes.

For instance, if we wanted to transform an array into an ArrayList with the generic filled, this utility could help:

public class SampleUtility {
   public static <T> List<T> toList(T...elements){
      return Arrays.asList(elements);
   }
}

In fact, method generics are used for the Arrays.asList() implementation! Look at Arrays.java in JDK 8:

public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a);
}

And that allows simple calls to the function:

public static void main(String[] cheese){
    List<Integer> integers = SampleUtility.toList(4, 8, 15, 16, 42);
    List<String> strings = SampleUtility.toList("Why", "not", "Zoidberg?");
}

Short, concise, and efficient. Much better!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s