0

I have a generic Container which holds objects of type E:

public class Container<E> {

    private final E e;

    public Container(E e) {
        this.e = e;
    }

    public E get() {
        return e;
    }
}

I have a bucket which holds various containers:

public class Bucket {
    private final Map<String, Container<?>> containers = new HashMap<>();

    public void put(String name, Container<?> container) {
        containers.put(name, container);
    }

    public Container<?> get(String name) {
        Container<?> container = containers.get(name);
        return container;
    }
}

I want to be able to put containers (of various types) into the bucket and retrieve them back in a type-safe way.

    Container<Long> longs = new Container<>(100L);
    Container<String> strings = new Container<>("Hello");

    Bucket bucket = new Bucket();
    bucket.put("longs", longs);
    bucket.put("strings", strings);

But as you can I lost type-safety:

   Container<?> longs1 = bucket.get("longs");
   Container<?> strings1 = bucket.get("strings");

I can't seem to figure out what it'd take that would allow me achieve the following:

   Container<Long> longs1 = bucket.get("longs");
   Container<String> strings1 = bucket.get("strings");

Keerthi
  • 466
  • 6
  • 12
  • Related: [In Java is it possible to create a type-safe Map of classes to instances of their class?](https://stackoverflow.com/questions/6795025/in-java-is-it-possible-to-create-a-type-safe-map-of-classes-to-instances-of-thei) – Mark Rotteveel Dec 05 '20 at 14:25
  • You can make the assignment in the last snippet work but only by casting at the right place, basically overwriting the uncertainty the compiler has. Basically you cannot achieve what are aiming for. – luk2302 Dec 05 '20 at 14:28

1 Answers1

0

My solution. I guess it serves my need pretty good:

public class Container<E> {

    private final E e;

    public Container(E e) {
        this.e = e;
    }

    public <T> T get(Class<T> target) {
        if (target.isAssignableFrom(e.getClass())) {
            return (T) e;
        }
        throw new ClassCastException(e.getClass().getName() + " '" + e + "' cannot be converted to " + target.getName());
    }
}
public class Bucket {
    private final Map<String, Container<?>> containers = new HashMap<>();

    public void put(String name, Container<?> container) {
        containers.put(name, container);
    }

    public Container<?> getContainer(String name) {
        return containers.get(name);
    }
}

Putting it to test:

Container<Long> longs = new Container<>(100L);
Container<String> strings = new Container<>("Hello");

Bucket bucket = new Bucket();
bucket.put("longs", longs);
bucket.put("strings", strings);

Container<?> longContainer = bucket.getContainer("longs");
Long aLong = longContainer.get(Long.class);
log.debug("{}", aLong); // Prints 100

Container<?> stringContainer = bucket.getContainer("strings");
String aString = stringContainer.get(String.class);
log.debug("{}", aString); // Prints Hello

log.debug("{}", stringContainer.get(Long.class)); // Throws java.lang.ClassCastException: java.lang.String 'Hello' cannot be converted to java.lang.Long
Keerthi
  • 466
  • 6
  • 12