1

I have an interface which is implemented by two classes

interface I {}
class A implements I {}
class B implements I {}

Now I want to create a map that can have key as Class of any object implemented with I and value as Object of that Class. So that I can do this :

map.put(A.class, new A());
map.put(B.class, new B());

And all of the below cases should give error :

map.put(A.class, new B());
map.put(I.class, new B());

The following Map is invalid this way :

Map<Class<? extends I>, ? extends I> map;

because first capture is different from second. How can I get the desired Map?

afzalex
  • 8,598
  • 2
  • 34
  • 61
  • Related question: http://stackoverflow.com/questions/416540/java-map-with-values-limited-by-keys-type-parameter – Keppil Oct 08 '15 at 06:55

3 Answers3

1

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.

michaelsnowden
  • 6,031
  • 2
  • 38
  • 83
  • But the point is I should not be able to insert any value in map having key of different class. Map should not allow it. And I told that `Map, ? extends MyInterface> map` is invalid. I want a correct `Map`. – afzalex Oct 08 '15 at 07:02
  • @afzalex See the edit. I believe this does what you're looking for – michaelsnowden Oct 08 '15 at 07:08
  • Please try to understand. map is still able to hold **A.class as key and new B() as value**. So if I will give this map to anybody. I cannot be sure that he will be inserting corresponding instance of Class – afzalex Oct 08 '15 at 07:12
  • I am not sure how to achieve this. The work around : instead of directly putting objects in map, have a method and run your rule – Ravindra babu Oct 08 '15 at 07:53
0

Does this code solve your purpose?

import java.util.*;

public class Test {
Map< Class<? extends I>,  I> map = new HashMap < Class<? extends I>, I>();
public static void main(String args[]){
    Test t = new Test();
    t.addMaps();

}
private void addMap(I obj){
    map.put(obj.getClass(),obj);
}
private void addMaps(){
    addMap(new A());
    addMap(new B());
    System.out.println(map);
}

}
interface I {}
class A implements I {}
class B implements I {}
class C{};
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
-1

ClassToInstanceMap in Guava does exactly what you want, and is type-safe.

thecoop
  • 45,220
  • 19
  • 132
  • 189