Explicit type parameters for generic methods

Explicit type parameters for generic methods

Generic methods provide a great convenience that the compiler does the type inference on them. In most cases, we can call a generic method without specifying actual types for the type parameters. For example, the code

  1. Map<String, String> map = Collections.emptyMap();
  2. Map<String, Boolean> map = Collections.emptyMap();

compiles cleanly, without any unchecked warnings. The convenience provided by generic methods is one of the incentives to use the generic static factory method, the generic singleton factory, etc (Effective Java Second Edition, Item 27).

However, there are some cases the type inference does not work. When the compiler can not infer the types, it gives some vague compile errors. For example, the generic method

  1. public static <K, V> Map<K, V> toHashMap(Map<K, V> map) {
  2.   return new HashMap<K, V>(null == map ? Collections.emptyMap() : map);
  3. } // does not compile!

yields the following compile error on line 2:

The constructor HashMap<K,V>(Map<capture#1-of ? extends Object
    ,capture#2-of ? extends Object>) is undefined

which is a sign of the type inference failure. Without diving into the Java Language Specification, I guess this is because methods can be overloaded, and there is no specific return type for Collections.emptyMap() to infer the type parameters from.

Anyhow, whenever the type inference does not work, we have to explicitly specify type parameters. The above generic method can be fixed as follows:

  1. public static <K, V> Map<K, V> toHashMap(Map<K, V> map) {
  2.   return new HashMap<K, V>(null == map ? Collections.<K, V> emptyMap() : map);
  3. }

Note the angle brackets after the dot operator.

Now let us have a look at another more real example where the type inference does not work: Whenever we want to use EasyMock to mock a method with generic types in the parameter list, we need to specify explicit type parameters. Say, we want to mock the interface

  1. public interface FooBar {
  2.   Map<String, String> fooBar(Map<String, String> map);
  3. }

We write the code

  1. @Test
  2. public final void test() {
  3.   // ...
  4.   FooBar fooBar = EasyMock.createMock(FooBar.class);
  5.   EasyMock.expect(fooBar.fooBar(EasyMock.<Map<String, String>> anyObject()))
  6.       .andReturn(null);
  7.   // ...
  8. }

The reason for explicitly specifying the type parameter is that EasyMock.anyObject() usually appears in the parameters of a mocked method, where type inference does not work according to what we have shown above. Had we not specified the explicit type parameter, the code would not have compiled.

(P.S. Using EasyMock.anyObject() outside EasyMock.expect() is catastrophic. You will find test failures from random places, you will find Eclipse build succeeds but Maven build fails, etc.)

(P.S. There are two overloadings, public static <T> T anyObject() and public static <T> T anyObject(Class<T> clazz), in EasyMock. The former can be used for all the types including reifiable and non-reifiable while the latter is only for reifiable types. Technically, types that are available at runtime are reifiable, e.g., Map is reifiable because its runtime information is available via Map.class; Types that are not available at runtime are non-reificable, e.g., Map<String, String> is non-reifiable because it is erased to Map at runtime, there is no such thing as Map<String, String>.class. As a rule of thumb, reifiable types have corresponding class literals and can be used with the instanceof operator. This is the reason why the second overloading, which takes a class literal as the parameter, is considered to be written for reifiable types.)

In summary, the type inference makes no difference between calling a generic method and calling an ordinary method in most cases. But there are some cases the type inference does not work, such as calling a parameterless generic method inline in the parameters of another method. In these cases, we need to specify explicit type parameters, otherwise there will be compile errors.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

More information about formatting options

To prevent automated spam submissions leave this field empty.