3

I am new in java generics and facing following issues. I have have method like,

private static void fillDescriptiveData(HashMap<String, Object> output, String attributeMapping) {
    for (Map.Entry<String, Object> outputInEntry : output.entrySet()) {
        String outputKey = outputInEntry.getKey();
        String outputValue = outputInEntry.getValue().toString();
        outputValue = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
        outputInEntry.setValue(outputValue);
    }
}

Now if I call API as below way

HashMap<String, Object> ObjectMap = new HashMap<String, Object>();
HashMap<String, List> listMap = new HashMap<String, List>();
  • fillDescriptiveData(ObjectMap,"here");
    this one working fine.

  • fillDescriptiveData(listMap,"here"); this call gives error

The method fillDescriptiveData(HashMap, String) in the type CustomAttribute is not applicable for the arguments (HashMap, String)`

why ?

In row to solve this issue I encounter with one more issue,

private static void fillDescriptiveData(HashMap<String, ? extends Object> output, String attributeMapping) {
    for (Map.Entry<String, ? extends Object> outputInEntry : output.entrySet()) {
        String outputKey = outputInEntry.getKey();
        String outputValue = outputInEntry.getValue().toString();
        outputValue = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
        outputInEntry.setValue(outputValue); /* Error comes at this line */
    }
}

HashMap<String, ? extends Object> ObjectMap = new HashMap<String, Object>();
HashMap<String, List> listMap = new HashMap<String, List>();
fillDescriptiveData(ObjectMap,"here");
fillDescriptiveData(listMap,"here");

error at line - outputInEntry.setValue(outputValue);

The method setValue(capture#4-of ? extends Object) in the type Map.Entry is not applicable for the arguments (String)

why ?

What is the best way to avoid this issues ?

3
  • 1
    Your second "Why?" asks "Why can't I put a String as a value in a Map<String, List> and the answer is "because the static type system is doing its job". Commented Sep 28, 2016 at 11:55
  • 2
    what you are trying to do is clearly bad anyway, your method fillDescriptiveData convert your HashMap<String, Whatever> to a HashMap<String, String> which is clearly a violation, don't use generics then, simply use raw type if you want to convert a tomato into a carrot and put the result into the same collection Commented Sep 28, 2016 at 11:59
  • How about something like Map<String, String> getDescriptiveData(HashMap<String, ? extends Object> output, String attributeMapping) {...}? - Also, your question's title is misleading; you may want to edit it. Commented Sep 28, 2016 at 12:39

2 Answers 2

2

This is the case when you could use type variables:

private static <T> void  fillDescriptiveData(Map<String, T> output,String attributeMapping)
{
    for(Map.Entry<String, T> outputInEntry : output.entrySet())
    {
        String outputKey = outputInEntry.getKey();
        String outputValue = outputInEntry.getValue().toString();
        outputValue = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
        outputInEntry.setValue((T) outputValue);
    }
}

More specifically, your second type-parameter in the map is unbounded. Object will not work here as it is specific class. ? extends Object is somewhat nonsense. Just HashMap<String, ?> would work until you will just read the map, but you will not be able to put something here. So only one way - using type variable.

EDIT: One more thing: please, use interfaces where it's possible. So here instead of HashMap<String, T> better use Map<String, T>. It isn't a mistake, just good and proper style of code.

Sign up to request clarification or add additional context in comments.

10 Comments

Apparently you expect the String outputValue to be castable into an arbitrary T.
@MarkoTopolnik I agree, this is quite strange, I didn't noticed that. But it will work, because in runtime you can put any values into the Map :)
The method signature that makes sense is Map<String, ? super T> map, T value), but this doesn't seem to be what OP is going for. He seems to be going for something quite ill-conceived.
"? extends Object is somewhat nonsense" not really it is an unknown object type, it is equivalent to ?
We call this a pyrrhic victory :)
|
1

The error with this line:

outputInEntry.setValue(outputValue);

Is that you're always putting a string into the entry. This will only work if the entry is of type ? super String, or exactly String. So it will not work for a Map<String, Object> or Map<String, List>.

It seems like you just want to map each value to a string. You can do it, but to be type safe, you need to create a new Map<String, String>. Since you're always mapping to a String.

If you for instance pass in a Map<String, List<?>> and (unsafely) replace all the values with strings. Someone could still keep using the Map<String, List<?>> that was passed into the function, but it now contains strings as values instead of lists. When they try to retrieve a List from it they get a class cast exception.

Something like this:

private static Map<String, String> fillDescriptiveData(HashMap<String, ?> input,
        String attributeMapping) {        
    Map<String, String> output = new HashMap<>();

    for(Entry<String, ?> e : input.entrySet()) {
            String outputKey = e.getKey();
            String outputValue = e.getValue().toString();
            outputValue
                = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
            output.put(outputKey, outputValue);
    }
    return output;
}
Map<String, String> r1 = fillDescriptiveData(ObjectMap, "here");
Map<String, String> r2 = fillDescriptiveData(listMap, "here");

2 Comments

This statement "This will only work" is not accurate. As I told in my answer it will work with type variable. Actual mismatch of types isn't critical problem as in runtime map can hold values of any types
@Andremoniy Of course, maybe I should have added "properly" or "in a type safe manner".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.