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?
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.
container
field is private, thus when thinking about type safety, you only have to check the code inside this class.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.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.