What about
interface I {
}
class A implements I {
}
class B implements I {
}
public class Example {
public static <T extends I, M extends Map<Class<T>, T>> void put(M map, Class<T> key, T value) {
map.put(key, value);
}
public static void main(String[] args) {
Map map = new HashMap<Class<I>, I>();
put(map, A.class, new A());
put(map, A.class, new B()); // error
put(map, B.class, new A()); // error
put(map, B.class, new B());
}
}
If you don't mind having errors at runtime, you could do this:
interface I {
}
class A implements I {
}
class B implements I {
}
public class MyMap implements Map<Class<? extends I>, I> {
private final Map<Class<? extends I>, I> map;
public MyMap(Map<Class<? extends I>, I> map) {
this.map = map;
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public I get(Object key) {
return map.get(key);
}
public I put(Class<? extends I> key, I value) {
assert key.getClass().isAssignableFrom(value.getClass());
return map.put(key, value);
}
public I remove(Object key) {
return map.remove(key);
}
public void putAll(Map<? extends Class<? extends I>, ? extends I> m) {
map.putAll(m);
}
public void clear() {
map.clear();
}
public Set<Class<? extends I>> keySet() {
return map.keySet();
}
public Collection<I> values() {
return map.values();
}
public Set<Entry<Class<? extends I>, I>> entrySet() {
return map.entrySet();
}
public static void main(String[] args) {
MyMap map = new MyMap(new HashMap<Class<? extends I>, I>());
map.put(A.class, new A());
map.put(A.class, new B()); // runtime error
map.put(B.class, new A()); // runtime error
map.put(B.class, new B());
}
}
It's not possible to get compile-time errors like this if map
implements Map
:
put(map, A.class, new A());
put(map, A.class, new B()); // error
put(map, B.class, new A()); // error
put(map, B.class, new B());
That's because there is no way to specify V
as being dependent on K
using the Map
interface. You will have to either provide your clients with a class that has runtime errors and implements Map, or a class that has compile-time errors, and doesn't implement Map.