Question

Item #29 in Effective Java presents a way to implement "typesafe" heterogeneous collections, which basically boils down to something like this:

public Class HeterogeneousContainer {
    private Map<Class<?>, Object> container = new HashMap<>();

    public <T> put(Class<T> type, T instance) {
        container.put(type, instance);
    }

    public <T> T get(Class<T> type) {
        //essentially just 
        //      return (T) container.get(type);
        return type.cast(container.get(type));
    }
}

Which (I presume) is then to be used like this (by a producer):

List<HeterogeneousContainer> resultSet = new ArrayList<>();
...
resultSet.add(new HeterogeneousContainer());
...
resultSet.get(rowIndex).add(Column1.class, column1Value);
resultSet.get(rowIndex).add(Column2.class, column2Value);
...

and like this (by the consumer):

for(HeterogeneousContainer row:resultSet) {
    Column1 column1Value = row.get(Column1.class);
    Column2 column2Value = row.get(Column2.class);
    ...
    //do something with the columnValues
}

My question now is why is this considered type safe? How is this any better than just putting column names into the map or just using a regular List/List<Object> and looking the columns up by index?

Does this effectively/practically in any way improve on the .getString/.getInt/.getXXXX approach of JDBC ResultsSets?

Was it helpful?

Solution

This class is considered typesafe, because even while it's doing class casting during runtime, this unsafe behavior is encapsulated, and you can't observe it.

  • The container field is private, thus when thinking about type safety, you only have to check the code inside this class.
  • The put method inserts a pair of the type tag T and an element of the type T. This is the only place where the container is updated, and it is guaranteed (at compile time) that the inserted element has the correct type tag.
  • The get method does class casting in an unsafe way (return type.cast(container.get(type))), but as long as it is assured that the container is modified only via put, it can not fail. And above we saw that put is of course the only way to do that, this class is typesafe for its users.

To tell the truth, if you really want, you can break the type safety of this class for example by using reflection. Usually this does not matter, as you want to defend your class for being accidentally misused rather than intentionally corrupted. However, as you read Effective Java further, you will see that defending against such attacks is much more complicated.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top