Sunday, January 18, 2009

Bookmark and Share

With the advent of generics in Java 5, also generic methods were introduced. Generic methods have one ore more type parameters, which can be used to specify the parameters or return type of a method. For example the JDK's collection utility class java.util.Collections declares a wide range of generic methods.

When using those methods, the concrete types to be used as parameters or return value are derived from the invocation context using type inference (Angelika Langer goes into detail on this topic in her great Generics FAQ here).

So for instance you can use the method emptyMap() of the mentioned class Collections as follows to initialize an empty Map<String, Long>:

1
2
3
4
5
6
7
8
9
public class GenericMethodsTest {

    @Test
    public void testTypeInference() {

        Map<String, Long> map = Collections.emptyMap();
        assertTrue(map.isEmpty());
    }
}

Unfortunately, this doesn't work, if the return value of the generic method isn't assigned to a variable, but rather is used as parameter for another method invocation. So the following code fails with a compiler error at line 6 saying "The method doSomething(Map<String,Long>) in the type GenericMethodsTest is not applicable for the arguments (Map<Object,Object>)":

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericMethodsTest {

    @Test
    public void testTypeInference() {

        doSomething(Collections.emptyMap()); //compiler error
    }

    private void doSomething(Map<String, Long> map) {
        assertTrue(map.isEmpty());
    }
}

What I did till today in such a case was to assign the result of the generic method to a local variable and using this variable as parameter. This is somewhat ugly and actually not neccessary at all, as it is possible to specify the type parameters explicitely as follows:

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericMethodsTest {

    @Test
    public void testTypeInference() {

        doSomething(Collections.<String, Long> emptyMap());
    }

    private void doSomething(Map<String, Long> map) {
        assertTrue(map.isEmpty());
    }
}

Actually a practical but rather unknown feature of the Java language, which makes the code shorter while more readable at the same time. More information on explicitely specifying type parameters when calling generic methods can again be found at Angelika Langer's FAQ here.

2 comments:

Stefan Bley said...

Cool. Kannte ich bisher nicht!

Gunnar Morling said...

Hi Stefan,

schön, dass der Post Dir gefällt. Und Glückwunsch – Du bist der erste Kommentator in meinem Blog :-)

Grüße, Gunnar