hostnik
hostnik

Reputation: 2683

Proper use of generics with collection instance factory

I'm trying to do the following thing using Apache Commons Collections v4:

Map<Integer, List<String>> namesPerNumber = 
        MapUtils.lazyMap(
            new HashMap<Integer, List<String>>(), 
            FactoryUtils.instantiateFactory(ArrayList.class));

namesPerNumber.get(1).add("Mickey");

But I get the following compiler error at the lazyMap call:

The method lazyMap(Map<K,V>, Factory<? extends V>) in the type MapUtils is not applicable for the arguments (HashMap<Integer,List<String&t;>, Factory<ArrayList>)

Is there any proper way to use the factory for generating lists in a map? I tried also this:

Map<Integer, List<String>> namesPerNumber = 
            MapUtils.lazyMap(
                new HashMap<Integer, List<String>>(), 
                FactoryUtils.<List<String>instantiateFactory(ArrayList.class));

But then I get this error at the instantiateFactory call:

The parameterized method <List<String>>instantiateFactory(Class<List<String>>) of type FactoryUtils is not applicable for the arguments (Class<ArrayList>)

The only working solution I found is the following, but I find it ugly:

Map<Integer, List<String>> namesPerNumber3 = 
            MapUtils.lazyMap(
                new HashMap<Integer, List<String>>(), 
                new Factory<List<String>>() {
                    @Override
                    public List<String> create() {
                        return new ArrayList<String>();
                    }
                });

Any help appreciated.

Signed,
lostingenerics

Upvotes: 1

Views: 365

Answers (1)

Holger
Holger

Reputation: 298143

Due to type erasure, class literals support only reifiable types or raw types, so ArrayList.class represents the raw type ArrayList, not the intended ArrayList<String>.

One way to solve this, is by using one unchecked operation:

@SuppressWarnings("unchecked") Class<ArrayList<String>> type = (Class)ArrayList.class;

Map<Integer, List<String>> namesPerNumber = 
    MapUtils.lazyMap(
        new HashMap<Integer, List<String>>(), 
        FactoryUtils.instantiateFactory(type));

Note that the effect of @SuppressWarnings("unchecked") is intentionally limited to the single unchecked operation here.

Or you use

Map<Integer, List<String>> namesPerNumber = 
    MapUtils.lazyMap(
        new HashMap<Integer, List<String>>(), 
        FactoryUtils.prototypeFactory(new ArrayList<String>()));

instead.

If you are using Java 8, the best option is

Map<Integer, List<String>> namesPerNumber = 
    MapUtils.lazyMap(new HashMap<>(), () -> new ArrayList<>());

Upvotes: 3

Related Questions