I am trying to make a simple storage class, that will store different classes instances. The only almost correct way I managed to do is with unchecked type casting.
HashSet<T> result = (HashSet<T>) storage.get(s);
Can it be done without unchecked casting and without making the class generic (class Storage<T> { }
)?
import java.util.*;
import org.junit.*;
class Tests {
@Test
public static void main (String[] args) {
Storage storage = new Storage();
HashSet<Child1> child1Set = storage.get("child1");
HashSet<Child1> duplicateChild1Set = storage.get("child1");
Assert.assertNotNull(child1Set);
Assert.assertSame(child1Set, duplicateChild1Set);
HashSet<Child2> child2Set = storage.get("child2");
Assert.assertNotNull(child2Set);
Assert.assertNotSame(child1Set, child2Set);
}
}
class Storage {
public Map<String, HashSet<? extends Parent>> storage = new HashMap<>();
public <T extends Parent> HashSet<T> get(String s) {
HashSet<T> result = (HashSet<T>) storage.get(s);
if (result == null) {
result = new HashSet<>();
storage.put(s, result);
}
return result;
}
}
class Parent { }
class Child1 extends Parent { }
class Child2 extends Parent { }
You can do it using Class
objects as keys, rather than String
s. Here is a short example. For simplicity, I have not included extends Parent
- you can put those back.
public final class Storage {
private final Map<Class<?>, Set<?>> storage = new HashMap<>();
public <T> Set<T> get(Class<T> s) {
Set<T> result = (Set<T>) storage.get(s); // Unchecked cast
if (result == null) {
result = new HashSet<>();
storage.put(s, result);
}
return result;
}
}
It is not possible to eliminate the unchecked cast in a mixed type container like this. There is no way to specify that if the key has type Class<T>
, the value has type Set<T>
. However, as long as the user of the Storage
class does not ignore any type safety warnings, this is completely type safe.
To use the class you can do storage.get(Double.class).add(4.2);
, for example.