简体   繁体   中英

why HashMap<String, Object> not accept HashMap<String, List> instance?

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 ?

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 ? 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.

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 ? 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");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM