Effective Java : Typesafe Heterogeneous Container Pattern

By Yashwant
Nov 23rd, 2013
0 Comments
6396 Views
Whoa!! Don’t get shocked by the name which sounds complicated. This one is a famous example from Effective Java by Joshua Bloch. Think about each word separately and it will make sense. We need to design a collection implementation (container) which can store values of different type of objects (Heterogeneous) without losing the type of object (Typesafe). Well, still not clear? Take an example – You have a database table which has 3 columns of 3 different data types and you need to store one row from this table into a Map in java. (For simplicity I have kept the no of columns fixed, you can have a scenario where even the no of columns are also dynamic)
Effective JAVA : Typesafe Heterogeneous Container Pattern

Effective JAVA : Typesafe Heterogeneous Container Pattern

 
Example: Let’s have a row from Student table which contains Role No: Integer, Name: String, DateOfJoining: Date.
 
So how do we start about it? We all know Generics are used to parameterize the collections. Set and Maps are most famous examples like Set<String>, Map<Integer, String>. What do we get by using generics with collections? In one word – Compile Time Type Safety. Also it makes the code more readable because by looking at the collection declaration you can tell what type of data it is going to contain. All good here but still this type of declaration means Homogeneous data (to be honest it’s an advantage). If I say you can use generics to store heterogeneous data, you might laugh at me at first but that’s possible with a little tweak.
[linkad]
 
To store heterogeneous data, we need to parameterize the key instead of collection. This is possible because with Java 1.5, class Class is generified. The type of a class literal is no longer simply Class but Class<T>. For example: Integer.class is of type Class<Integer> and String.class is of type Class<String>. Now we can take advantage of this and make the key of our map parameterized by keeping it as Class<T> type and value as an instance of this type T. Let’s have a look at API of how our Student class API might look like
public class StudentTHC {
    public  T getInfo(Class key) ;
    public  void putInfo(Class key, T value);
}
And here is piece of code which uses above API. None of the output values are explicitly casted.
public static void main(String[] args) {

        StudentTHC student = new StudentTHC();
        student.putInfo(Integer.class, 1);
        student.putInfo(String.class, "Yash");
        student.putInfo(Date.class, new Date());
        Integer roleNo = student.getInfo(Integer.class);
        String name = student.getInfo(String.class);
        Date dateOfJoining = student.getInfo(Date.class);
        System.out.println("Role No " + roleNo + ", " + name + " joined the class on " + dateOfJoining);
    }

 
Usage is straightforward but you might be thinking the actual implementation of pattern would be somewhat complicated and large. Well, thanks to generics the implementation class is pretty small. It contains a class variable studentInfo Map which stores all the data.
public class StudentTHC {

    Map<Class<?>, Object> studentInfo = new HashMap<Class<?>, Object>();

    public  T getInfo(Class key) {
        if(key == null) {
            throw new NullPointerException("No null keys are allowed in this coede");
        }
        return key.cast(studentInfo.get(key));
    }

    public  void putInfo(Class key, T value) {
        studentInfo.put(key, value);
    }
}
Code is fairly small but here are certain points we need to note 
 
1. Map<Class<?> , Object>: Here <?> denotes that key of map can be of any generic class type. As mentioned earlier, important point in this implementation is that, key is parameterized and not the collection.
 
2. Map<Class<?>,  Object>: Since key is parameterized, values would be heterogeneous so best way to store them is to use Object type.
 
3. public <T>  T getInfo(Class<T> key): Return type of method is generified here. This method will return value of type of class passed as key.
 
4. return key. cast(studentInfo.get(key))cast  method is a method added in Class class in java 1.5. It does the same work as manually casting any object to the type of class object on whose instance method is called. If a non matching type is present, it throws ClassCastException. In our case, since we have stored the class type as key, we are sure the value would be of same class and hence it’s safe to dynamically cast the return value.
[linkad]
 
Interesting implementation but there are certain limitations
 
1. Don’t use raw type collections. It might simply blow the program. By raw type I mean collection declaration without generics. You can pass Map as a method parameter and store a String into Map which could have been defined to store Integers. It’s a developer’s responsibility not to use collections without generics for type safety. (Most of the IDEs give you a warning with unchecked code now, so you better not ignore those warnings.)
 
2. List<Integer> and List<String> both are of type List.class. List<Integer>.class and List<String>.class both are wrong syntactically. 
 
This article is based on topic no 29 from Effective Java by Joshua Bloch. Hope you enjoyed reading this. You can download sample code here.

About the Author

- An avid reader and a big time comics fan. A software engineer by profession who loves writing code and make things work.

Leave a Reply

Your email address will not be published. Required fields are marked *

facebook comments: