javagenericsheterogeneous

Parsing heterogeneous map


I have a heterogeneous map which exists in JSON format and I would like to parse it and turn it into a heterogeneous map object (class HeterogeneousMap).

To parse the map I use an object which defines all known keys the map can have (class HeterogeneousMapStructure).

The MapKey<T> interface has the method T parseValue(JsonReader jsonReader) to parse the value of the key.

The problem I am having is how to put the parsed value in the type safe heterogeneous map object:

public class HeterogeneousMap {
    public <T> void put(MapKey<T> mapKey, T value) {
        // Put key and value in map
    }
}

public interface MapKey<T> {
    T parseValue(JsonReader jsonReader) throws IOException;
}

public class HeterogeneousMapStructure {
    private final List<MapKey<?>> keyList;

    public HeterogeneousMap parseMap(JsonReader jsonReader) {
        HeterogeneousMap heterogeneousMap = new HeterogeneousMap();

        // ... find matching key
        MapKey<?> matchingMapKey = ...;
        /*
         * Compiling error:
         * The method put(TestClass.MapKey<T>, T) in the type TestClass.HeterogeneousMap 
         * is not applicable for the arguments (TestClass.MapKey<capture#1-of ?>, capture#2-of ?)
         */
        heterogeneousMap.put(matchingMapKey, matchingMapKey.parseValue(jsonReader));

        return heterogeneousMap;
    }
}

Is there a way to solve this?


Solution

  • There is a way to workaround.

    You can create a seperate method for that:

    private static <T> void parseAndPut(HeterogeneousMap map, MapKey<T> key, JsonReader in) throws IOException {
        map.put(key, key.parseValue(in));
    }
    

    and call this method in your parse map:

    public class HeterogeneousMapStructure {
        private final List<MapKey<?>> keyList;
    
        public HeterogeneousMap parseMap(JsonReader jsonReader) {
            HeterogeneousMap heterogeneousMap = new HeterogeneousMap();
    
            // ... find matching key
            MapKey<?> matchingMapKey = ...;
            /*
             * Compiling error:
             * The method put(TestClass.MapKey<T>, T) in the type TestClass.HeterogeneousMap 
             * is not applicable for the arguments (TestClass.MapKey<capture#1-of ?>, capture#2-of ?)
             */
            parseAndPut(heterogeneousMap, matchingMapKey, jsonReader);
    
            return heterogeneousMap;
        }
    
        private static <T> void parseAndPut(HeterogeneousMap map, MapKey<T> key, JsonReader in) throws IOException {
            map.put(key, key.parseValue(in));
        }
    }
    

    This should work, but I don't have all your classes so I can't really test them. If they got something wrong, tell me and I will try to fix it.


    How I come up with this solution

    The problem lies in the type wildcard, or say existential type if you can understand. Java is quite stupid with these wildcards. The compiler treat any two wildcard as possible distinct type, even if it can infer them to be the same type or they are in the same type hierarchy. In your case, the two capture#X-of ? is considered not related, so a compile error is raised.

    Since the problem is due to the compiler being unable to infer, we tell it explicitly. As you can see, I extract the operation and assign the existential type to be a type variable (T), so compiler will now be able to see the type matches, and the problem is solved.