Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Declaring a Map with two related generics Types (<T> Map<Class<? extends ClassA<T>>,Class<? extends ClassB<T>>>)

Tags:

java

generics

Is it possible to declare a Map that maps from keys of a specific subclass to values of a specific subclass but ensuring that both classes share the same Type parameter?

For the background:

both ClassA and ClassB implement behaviour for a common resource

public abstract  class ClassA<T> {
      public abstract T getResource() ;
}

public abstract class classB<T> {
       public  abstract void consoumeResource(T resource);
}

i want to map from implementations of ClassA and ClassB and ensure only "compatible" pairs can be put together in one entry.

like image 279
DThought Avatar asked Jan 23 '13 10:01

DThought


People also ask

What is the difference between List <? Super T and List <? Extends T?

extends Number> represents a list of Number or its sub-types such as Integer and Double. Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object.

What does <? Super t mean in Java?

super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .

How do you initialize a generic map in Java?

Suppose if the key is of type String and the corresponding value is of type Integer, then we can initialize it as, Map< String , Integer > map = new HashMap< String ,Integer >(); The map can now only accept String instances as key and Integer instances as values.

What is super and extends in generics in Java?

super is a lower bound, and extends is an upper bound.


2 Answers

Another way would be to provide your own Map implementation. There's not much code needed if you extend an existing implementation, and use your new type:

public class CompatibleHashMap<T> extends HashMap<ClassA<T>, ClassB<T>> {

}

Now, a CompatibleHashMap<String> only lets you put ClassA<String> as keys and ClassB<String> as values.

EDIT:

As you mentioned in your comment, this way you are tying yourself to a Map implementation. You can overcome this by doing something like the following:

public class CompatibleMap<T> implements Map<ClassA<T>, ClassB<T>> {

    private Map<ClassA<T>, ClassB<T>> map;

    public CompatibleMap(Map<ClassA<T>, ClassB<T>> map) {
        this.map = map;
    }

    @Override
    public Set<List<T>> keySet() {
        return map.keySet();
    }
    // ... implement all other Map methods by calling the method on map.
}

You can then instantiate it like

CompatibleMap<String> map = new CompatibleMap<>(new HashMap<ClassA<String>, ClassB<String>>());

This way, you are not tied to a specific Map implementation, and the compiler will throw an error if the generic types of the map, ClassA and ClassB are not the same.

like image 131
jlordo Avatar answered Oct 07 '22 01:10

jlordo


You can't do this in the Map declaration but you can do this using methods which access/update the map.

e.g.

private final Map<Class, Builder> builderMap = new LinkedHashMap<>();

public <T> void addBuilder(Class<T> tClass, Builder<T> tBuilder) {
     builderMap.put(tClass, tBuilder);
}

public <T> Builder<T> getBuilderFor(Class<T> tClass) {
    @SuppressWarnings("unchecked") 
    Builder<T> tBuilder = (Builder<T>) builderMap.get(tClass);
    return tBuilder;
}
like image 40
Peter Lawrey Avatar answered Oct 07 '22 01:10

Peter Lawrey