d
WE ARE EXPERTS IN TECHNOLOGY

Let’s Work Together

n

StatusNeo

Typesafe Heterogeneous Container Classes in Java using Generics

We all know that, common uses of generics includes creating typesafe parametrized collections, such as Set<T> and Map<K,V>, and single-element containers, such as ThreadLocal<T> and AtomicReference<T>. In all of these uses, it is the container that is parameterized.

If you think for a moment, this limits you to a fixed number of type parameters per container. Normally, that is exactly what you want. A Set has a single type parameter, representing its element type; a Map has two, representing its key and value types; and so forth.

Sometimes, however, you need a little more flexibility. For example, a database row can have arbitrarily many columns, and it would be nice to be able to access all of
them in a typesafe manner or something like the spring container. Luckily, there is an easy way to achieve this effect. The idea is to parameterize the key instead of the container. Then present the parameterized key to the container to insert or retrieve a value. The generic type system is used to guarantee that the type of the value agrees with its key.

Preview in new tab(opens in a new tab)

A simple example of this approach :

Consider a Favorites class that allows its clients to store and retrieve a favorite instance of arbitrarily many types !

The Class object for the type will play the part of the parameterized key. The reason this works is that class Class is generic. The type of a class literal is not simply Class, but Class<T>. For example, String.class is of type Class<String>, and Integer.class is of type Class<Integer>. When a class literal is passed among methods to communicate both compile-time and runtime type information, it is called a type token

Here’s a representation of this class :

The API for the Favorites class is simple, and looks like the code snippet below :

public class Favorites {
    public <T> void putFavorite(Class<T> type, T instance);
    public <T> T getFavorite(Class<T> type);
}

Implementation of the same :

public class Favorites {

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

    public <T> void putFavorite(Class<T> type, T instance) {
    favorites.put(Objects.requireNonNull(type), type.cast(instance));
}

public <T> T getFavorite(Class<T> type) {
        return type.cast(favorites.get(type));
    }
}

As you can see, the class consists of a Map whose key is of type Class with an unbounded wildcard. This means that every key can have a different parameterized type: one can be Class, the next Class, and so on. That’s where the heterogeneity comes from.

Insights on the Implemented class :

The implementation uses cast(Object obj) method. This method is a part of the class , Class<T>’s API. This signature of the method takes full advantage of the fact that the Class is generic and the return type is of the Class object. Below is the signature of the same

public class Class<T> {
      T cast(Object obj);
}

Additionally, there are a few more insights :

  1. The Type of values in favorites map is simply Object
    • In other words, the Map does not guarantee the type relationship between keys and values, which is that every value is of the type represented by its key
    • We take into account this fact, while executing putFavorite method as we dynamically try to cast the instance reference to the given class type
    • Additionally while retrieving a “favorite” in the getFavorite method, we dynamically cast the value being returned to the type represented by the class object, hence establishing type-safety
  2. Limitations with the class
    • Favorites class cannot store non-reifiable type ( this scary sounding word, simply means non-primitive (Wrapper classes) types for e.g. : Integer, List<T> etc. )
    • If you try to store your favorite List<String>, your program won’t compile. The reason is that you can’t get a Class object for List<T>. The class literal List.class is a syntax error. And it’s a good thing, too. List<Integer> and List<Integer> share a single Class object, which is List.class. It would wreak havoc with the internals of a Favorites object if the “type literals” List.class and List.class were legal and returned the same object reference

References

  1. Effective Java (3rd Edition) – Joshua Baloch
  2. https://docs.oracle.com/en/java/

Add Comment