Teaser Image

qnoid

Markos Charatzas - Edinburgh, UK




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);      
        }
    }

Joshua Bloch asked us to consider typesafe heterogenous containers. You should do well to read about them but loosely speaking you can think of such a data structure as a list. A heterogenous that is.

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.

Source