Question

The following code is an example of what I hope to achieved. Unfortunately, there is no way to compile this code without a cast in the function getElements().

I still have quite wrapped my head around Java generics completely, but this seems like a fairly simply case to get around.

Any help would be greatly appreciated.

import org.junit.Test;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class GenericFruitTest
{
    @Test
    public void genericFruit()
    {
        Collection<Apple> apples = new FruitBasket().getApples();
        Collection<Orange> oranges = new FruitBasket().getOranges();

        Collection<Rye> rye = new BreadBasket().getRye();
    }

    class FruitBasket extends Basket<Fruit>
    {
        Collection<Apple> getApples()
        {
            return getElements(Apple.class);
        }

        Collection<Orange> getOranges()
        {
            return getElements(Orange.class);
        }
    }

    private class BreadBasket extends Basket<Bread>
    {
        private Collection<Rye> rye;

        public Collection<Rye> getRye()
        {
            return getElements(Rye.class);
        }
    }

    class Basket<E>
    {

        Map<Class<? extends E>, Collection<? extends E>> classCollectionMap = new HashMap<Class<? extends E>, Collection<? extends E>>();

        <T extends E> Collection<T> getElements(Class<T> elementClass)
        {
            // The following line throws compile time "incompatible types" without cast
            return classCollectionMap.get(elementClass);
        }

    }

    class Rye extends Bread
    {
    }

    class Apple extends Fruit
    {
    }

    class Orange extends Fruit
    {
    }

    class Fruit
    {
    }

    private class Bread
    {
    }
}
Was it helpful?

Solution 2

It requires a casting because the compiler doesn't know the type of the collection that any given key has. Your map says that it maps a Class of an unkwnown type to a Collection of an unknown type, but you have no way of telling the map that they have to be the same type.

When working with generics, most of the time you can't get rid of the casts. Most of those casts are, however, unchecked conversions that are removed at runtime because of type erasure. The point is that, when you can guarantee type safety, the compiler leaves you alone and clients of your class get a type-safe, casting-free interface.

Of course, if you screw up, the runtime has no way of alerting you.

OTHER TIPS

The Map in this case is mapping from Class<? extends E> to Collection<? extends E>. Any Class<? extends E> could legitimately as far as the compiler is concerned be put in mapping to any Collection<? extends E>.

The fact that you happen to have always put a class that matches the collection into the Map is not something the compiler is aware of - as you could put the class of anything that extends E into the map.

Unfortunately you are going to need the cast in this case - although at least generics lets you put the cast inside your method, so anyone using the method cannot see it.

Just like Template meta Programing in C++, You must specify a specific type, otherwise compiler does not know how to run in runtime.

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