Heterogenous Containers... Preamble
public interface Person { }
public class Designer implements Person { }
public class Developer implements Person { }
/**
* A heterogenous list
*/
public class HeterogenousList<E>
{
private final Map<Class<?>, E> elements =
new HashMap<Class<?>, E>();
public void add(E e) {
this.elements.put(e.getClass(), e);
}
public <T extends E> T get(Class<T> type)
{
E element = this.elements.get(type);
if(element == null){
throw new NoSuchElementException(String.format("No element of type: %s", type.getSimpleName()));
}
return type.cast( element );
}
}
/**
* Let's see how it feels like
*/
public class PersonsTest
{
@Test
public void homogeneous() throws Exception
{
Developer developer = new Developer();
Designer designer = new Designer();
List<Person> persons = new ArrayList<Person>();
persons.add( developer );
persons.add( designer );
int DEVELOPER_INDEX = 0;
int DESIGNER_INDEX = 1;
Person theDeveloper = persons.get(DEVELOPER_INDEX);
Person theDesigner = persons.get(DESIGNER_INDEX);
Assert.assertEquals(developer, theDeveloper);
Assert.assertEquals(designer, theDesigner);
}
@Test
public void heterogeneous() throws Exception
{
Developer developer = new Developer();
Designer designer = new Designer();
HeterogenousList<Person> persons = new HeterogenousList<Person>();
persons.add( developer );
persons.add( designer );
Class<Developer> DEVELOPER_INDEX = Developer.class;
Class<Designer> DESIGNER_INDEX = Designer.class;
Developer theDeveloper = persons.get(DEVELOPER_INDEX);
Designer theDesigner = persons.get(DESIGNER_INDEX);
Assert.assertEquals(developer, theDeveloper);
Assert.assertEquals(designer, theDesigner);
}
@Test(expected=NoSuchElementException.class)
public void noSuchElementException() throws Exception
{
HeterogenousList<Person> persons = new HeterogenousList<Person>();
persons.add( new Developer() );
Class<Designer> DESIGNER_INDEX = Designer.class;
persons.get(DESIGNER_INDEX);
}
public void incorrectType() throws Exception
{
HeterogenousList<Person> persons = new HeterogenousList<Person>();
//compile error
persons.get(String.class);
}
}
Since Java 5 with the introduction of generics you could specify the type of a list.
That way the compiler makes sure you don’t accidentaly add an object of different type than expected as well as you get the right one back when you ask for it.
However, you are still limited to the inherited type of the object you add.
Instead with a heterogenous container you get the actual type back.
The only difference is that the index of your list now becomes the type rather an int specifying its position.
The
T extends E
merely specifies that any type requested must be a subclass of the type specified in the list. This saves you from passing arbitrary types, yet you still need to accommodate asking for valid types that aren’t added to the list.
In the posts to come we’ll explore the possibilities of such containers.