Monday, November 10, 2008

Typedef in Java or Map<String, List<Map<String, Object>>>

Folks!

Have you ever had generic types like these Map<String, List<Map<String, Object>>>? If so this post might be interesting for you.

I had to pass either

Map<String,  Map<String, Object>>

or

Map<String,  List<Map<String, Object>>>

through a single method.

As you may know, polymorphism does not work with generics as the generic type is erased at compile time and is thus not available at run time.

So, I tried to have a decent signature for my method:

public void doSomething(Map<String, Object> data);
 
Which obviously works for both above types. Unfortunately, this signature works for many other types as well. Even worse, checking which type has been passed in, is really hard as the generic type has been erased. What you would have to do to really find out is to check the actual type of the map values. Really ugly. 

What can you do then? Before doing Java programming I was a C++ programmer, so I remembered the typedef keyword. Giving a lengthy type descriptor a short name - that's what I would have used in the C++ world. However, in the C++ world generics - templates in C++ speak - where different in nature. Each generic parameter would actually create a new type - which is not the case for Java. Apart from the fact that there is no typedef in Java! 

I finally did as Matthias advised me: Adding a thin wrapper around my two generic types that serves both as a short name as well as a type valid for run time:

A wrapper for Map<String, Object> would look like this:
public class DataProvider {

public final Map<String, Object> data;

public DataProvider(Map <String, Object> data) {
this.data = data;
}

public DataProvider() {
this(new HashMap<String, Object>());
}
}
Such a wrapper even saves you from typing constructors having weird syntax all the time as the default constructor already does this for you.

Concerning public fields, I know some people do not like them - for whatever reason. Simply make that field private or protected and add a getter method.