RandomQuestion
RandomQuestion

Reputation: 6988

Java generics - Map with generic type as key

I have following maps. Both maps hold instance of ClassA/ClassB and instance of CellProcessor as value.

Map<ClassA, CellProcessor> classAProcessors;
Map<ClassB, CellProcessor> classBProcessors;

I want to create a map which will store above maps as value and with key as class type of ClassA/ClassB so that when I do map.get(ClassA.class) it returns me Map<ClassA, CellProcessor> classAProcessors

I wrote this method to create final map but it does not seem to be working.

public static <T> Map<Class<T>, Map<T, CellProcessor>> getOutputProcessors(){
    Map<ClassA, CellProcessor> classAProcessors = getOutputCellProcessorsForClassA();
    Map<Class<T>, Map<T, CellProcessor>> processors = Maps.newLinkedHashMap();
    processors.put(ClassA.class, classAProcessors ); //error here
    return processors;
}

Compiler error:

The method put(Class<T>, Map<T,CellProcessor>) in the type Map<Class<T>,Map<T,CellProcessor>> is not applicable for the arguments (Class<ClassA>, Map<ClassA,CellProcessor>)

Could someone help me in understanding what's wrong here.

Thanks

Upvotes: 2

Views: 795

Answers (4)

Tomasz Szymulewski
Tomasz Szymulewski

Reputation: 2073

The compiler reports an error because you are trying to add to the map processors an entry with an incompatible type. At compile time the compiler does not know what the type of T is. Type T will be determined by the client which uses the method getOutputProcessors. Method processors.put expects that the first parameter is exactly the type of Class<T> (T will be determined only by code that uses the method getOutputProcessors), but you are always passing the same value of ClassA.class. The second parameter of the method processors.put is also incorrect, it should be exactly the type of Map<T, CellProcessor>, and you are passing permanently argument of type Map<ClassA, CellProcessor>. Treat type T as an independent type, so in the code of your method T is not equivalent to ClassA. The correct version of your method should look like this:

public static <T> Map<Class<T>, Map<T, CellProcessor>> getOutputProcessors(Class<T> key, Map<T, CellProcessor> classProcessors) {
    Map<Class<T>, Map<T, CellProcessor>> processors = new HashMap<Class<T>, Map<T, CellProcessor>>();
    processors.put(key, classProcessors);
    return processors;
}

You can use this method in the following manner:

Map<Class<ClassA>, Map<ClassA, CellProcessor>> processorsA = getOutputProcessors(ClassA.class, classAProcessors);
Map<Class<ClassB>, Map<ClassB, CellProcessor>> processorsB = getOutputProcessors(ClassB.class, classBProcessors);

But in this way the map processorsA and processorsB are not compatible and you will not be able to merge them into one map. So you need to use different type of map than Map<Class<T>, Map<T, CellProcessor>>.
The best way is to create (or not if it already exists) a super type for ClassA and ClassB (for example a class or interface with name ClassBase). getOutputProcessors method that uses superclass ClassBase looks as follows:

public static Map<Class<? extends ClassBase>, Map<? extends ClassBase, CellProcessor>> getOutputProcessors() {
    Map<Class<? extends ClassBase>, Map<? extends ClassBase, CellProcessor>> processors = 
            new HashMap<Class<? extends ClassBase>, Map<? extends ClassBase, CellProcessor>>();

    processors.put(ClassA.class, getOutputCellProcessorsForClassA());
    processors.put(ClassB.class, getOutputCellProcessorsForClassB());

    return processors;
}

If for some reason you can not have in your code superclass ClassBase then you can use a method that looks as follows:

public static Map<Class<? extends Object>, Map<? extends Object, CellProcessor>> getOutputProcessors3() {
    Map<Class<? extends Object>, Map<? extends Object, CellProcessor>> processors = 
            new HashMap<Class<? extends Object>, Map<? extends Object, CellProcessor>>();

    processors.put(ClassA.class, getOutputCellProcessorsForClassA());
    processors.put(ClassB.class, getOutputCellProcessorsForClassB());

    return processors;
}

I hope my answer will help you

Upvotes: 2

Wins
Wins

Reputation: 3460

public static Map<Class, Map<Object, CellProcessor>> getOutputProcessors() {
    Map<Class, Map<Object, CellProcessor>> processors = Maps.newLinkedHashMap();
    processors.put(ClassA.class, getOutputCellProcessorsForClassA);
    return processors;
}

Upvotes: 0

Sathya
Sathya

Reputation: 140

Why are you adding <T>, class is enough.

If at all you want to use <T> change it to and have ClassA and ClassB extends baseClass.

Upvotes: 0

robert_difalco
robert_difalco

Reputation: 4914

ClassA and ClassB cannot be in the same typed map. To do this you will need a map that is heterogenous (can have objects of any type) and as such it will need to be a HashMap with no type arguments (or ? or Object as an argument).

However, if ClassA and ClassB share the same supertype, then you can make a map for that shared supertype.

No if I am misunderstanding and you just want a map of maps for classA separate from classB then you need two methods. You can't overload implementations with generics the way you can with C++ templates.

So you will need to methods with different names one returning the map of maps for ClassA and the other for ClassB. They can't have the same name and be in the same class but specialized for different types.

Upvotes: 1

Related Questions