Javier Ferrero
Javier Ferrero

Reputation: 8811

Type safe heterogeneous container pattern to store lists of items

I'm trying to implement a type-safe heterogeneous container to store lists of heterogeneous objects.

I have seen several exameples of type-safe heterogeneous container pattern (link) but all of them store a single object of a type.

I have tryed to implement it as follows:

public class EntityOrganizer {  

    private Map<Class<?>, List<Object>> entityMap = new HashMap<Class<?>, List<Object>>();

    public <T> List<T> getEntities(Class<T> clazz) {
        return entityMap.containsKey(clazz) ? entityMap.get(clazz) : Collections.EMPTY_LIST;
    }

    private <T> void addEntity(Class<T> clazz, T entity) {
        List<T> entityList = (List<T>) entityMap.get(clazz);
        if(entityList == null) {
            entityList = new ArrayList<T>();
            entityMap.put(clazz, (List<Object>) entityList);
        }
        entityList.add(entity);
    }   
}

But the problem is this code is full of unchecked casts. Can someone help with a better way of implementing this?

Many thanks

Upvotes: 8

Views: 7819

Answers (3)

George Campbell
George Campbell

Reputation: 108

For very small lists you can encode a linked list of java generics.

And<UUID, And<Integer, Of<String>>> x = Tuple.of("test").and(2).and(UUID.randomUUID());

The definition of the types for And and Of are a bit mind bending. For brevity I've left out equals/hashCode.

import java.util.Objects;
import java.util.UUID;

public abstract class Tuple<T extends Tuple<T>> {
    public static final <E> Of<E> of(E e) {
        return new Of<>(e);
    }

    public abstract <E> And<E, T> and(E e);

    public static final class And<T, R extends Tuple<R>> extends Tuple<And<T, R>> {
        public final T t;
        public final R r;

        private And(T t, R rest) {
            this.t = t;
            this.r = rest;
        }

        public <N> And<N, And<T, R>> and(N next) {
            return new And<>(next, this);
        }
    }

    public static final class Of<T> extends Tuple<Of<T>> {
        public final T t;

        private Of(T t) {
            this.t = t;
        }

        public <N> And<N, Of<T>> and(N next) {
            return new And<>(next, this);
        }
    }
}

Upvotes: 0

humbletrader
humbletrader

Reputation: 414

You don't have to cast :

(List<T>) entityMap.get(clazz).

When you say

entityMap.get(clazz)

you actually have a List<Object> which is enough for your needs.

The same for

entityList = new ArrayList<T>();

You should just use entityList = new ArrayList<Object>();

Your type safety is ensured by the method declaration

<T> void addEntity(Class<T> clazz, T entity) {

and the use of Map having as key a Class.

So the code should look like :

 private <T> void addEntity(Class<T> clazz, T entity) {
            List<Object> entityList = entityMap.get(clazz);
            if(entityList == null) {
                entityList = new ArrayList<Object>();
                entityMap.put(clazz, entityList);
            }
            entityList.add(entity);
   } 

Upvotes: 1

irreputable
irreputable

Reputation: 45433

The question is, what is "unchecked cast"?

Sometimes casts are provably safe, unfortunately the proof is beyond javac's capability, which does only limited static analysis enumerated in the spec. But the programmer is smarter than javac.

In this case, I argue that these are "checked casts", and it's very appropriate to suppress the warning.

See 2 other related examples:

Heterogeneous container to store genericly typed objects in Java

Typesafe forName class loading

Upvotes: 8

Related Questions