Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make Java to infer the right generic map type for my typesafe map literals?

I am trying to write a typesafe map literal helper class in java that can generate maps of any type and have the compiler check that all given keys and values conform to the type of the map. For a map with two values there would be a function with the following signature:

public static <KEY, VALUE> Map<KEY, VALUE> make(final KEY key1,
        final VALUE value1,
        final KEY key2,
        final VALUE value2)

Now I would have expected that something like this would be possible:

    Map<Integer, Object> map = make(1, "bla", 3, 17);

However I get an compiler error:

Type mismatch: cannot convert from Map<Integer,Object&Serializable&Comparable<?>> to 
 Map<Integer,Object>

Is there a way to fix this? Of course, defining a function with signature make(Object... keysAndValues) would work, but I would loose the compile time typesafety. :-(

like image 927
Hans-Peter Störr Avatar asked Dec 17 '22 11:12

Hans-Peter Störr


2 Answers

The error is probably here:

Map<Integer, Object> map = make(1, "bla", 3, 17);
                                   ^^^^^     ^^

VALUE > Object can't be inferred from both String and Integer

Try any of these:

// Helping the compiler to infer Object for VALUE
Map<Integer, Object> map = make(1, (Object)"bla", 3, 17);

// Explicitly bind VALUE to Object
Map<Integer, Object> map = MyClass.<Integer, Object>make(1, "bla", 3, 17);

// If you don't need to write into "map" (as user pmnt also suggested)
Map<Integer, ?> map = make(1, "bla", 3, 17);
like image 113
Lukas Eder Avatar answered Jan 31 '23 01:01

Lukas Eder


Your code will work if you modify the Map's signature in the calling method:

No changes required to make

public static <A, B> Map<A, B> make(final A key1, final B value1,
        final A key2, final B value2) {
    Map<A, B> map = new HashMap<A, B>();
    map.put(key1, value1);
    map.put(key2, value2);
    return map;
}

Make's caller

You must change Map<String, Object> to Map<String, ? extends Object>:

public static void main(String[] args) {

    @SuppressWarnings("unused")
    Map<String, ? extends Object> m = make("a", new Integer(1), "2", "efg");

}

EDIT1: OpenJDK 1.6 + Eclipse Indigo compiler

EDIT2: When building generic maps like that, you must accept that you have to downcast when it comes to retrieving the values.

EDIT3: Is there a way to fix this? Of course, defining a function with signature make(Object... keysAndValues) would work, but I would loose the compile time typesafety.

You will always loose compile time safety at a certain point. At least when it comes to retrieval.

like image 43
home Avatar answered Jan 31 '23 01:01

home