How do I get Java to infer the correct general map type for my map literal types?

I am trying to write a class of the hedferometric literary letters class in Java that can generate cards of any type and check the compiler to ensure that all given keys and values ​​match the type of card. For a card with two values, there will 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 was expecting something like this to work out:

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

However, I get a compiler error:

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

Is there any way to fix this? Of course, defining a function with make(Object... keysAndValues) signatures make(Object... keysAndValues) would work, but I would lose features like compile time make(Object... keysAndValues)

+4
source share
6 answers

Perhaps the error is here:

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

VALUE > Object cannot be inferred from either String or Integer

Try any of these steps:

 // 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); 
+6
source

Your code will work if you change the signature of the card in the calling method:

No change required for 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 caller

Should you change Map<String, Object> to Map<String, ? extends Object> 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 creating such community cards, you have to accept that you need to downgrade when it comes to getting values.

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

At some point, you will always lose compile-time security. At least when it comes to extraction.

+3
source

This compiles:

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

This is what gives you typical Java type output. Not what you expected?

You have several options, or use a method clarifier:

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

Or you can use objects that do not have a common parent interface.

 Map<Integer, Object> map = make(1, "bla", 3, new Object()); 
+2
source

The compiler should "guess" the KEY and VALUE types from the given parameters (the types of your Map where you store the return value are ignored). Guess for VALUE - the strictest type, suitable for both Integer and String: everything that implements Comparable and Serializable ( Object&Serializable&Comparable<?> ).

You have two options: casting at least one VALUE value to an object:

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

or using a wildcard on the map

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

String actually implements the Serializable and Comparable , and Object does't .. so its saying Object does not implement, but its implicict typeCasted String does ...

0
source

The following solution looks like it should work, but, oddly enough, it only works in the Eclipse compiler, but not on the simple Java 6 compiler. 8- {Why ???

Declaring a method is annoyingly complicated, but allows the simplest syntax when creating maps:

 public static <KEY, AKEY extends KEY, VALUE, AVALUE extends VALUE> Map<KEY, VALUE> make2(final AKEY key1, final AVALUE value1, final AKEY key2, final AVALUE value2) 

Here the compiler takes the context out to

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

that KEY must be an integer, and VALUE must be an Object; AKEY is inferred from the given parameters as Integer and AVALUE as Object & Serializable & Comparable, and the compiler can verify that AKEY matches KEY and AVALUE for VALUE.

0
source

All Articles