views:

50

answers:

2

I've read the Beautiful code with Google Collections, Guava and static imports article about Java collections, and the following snippet got my attention:

Map<String, Map<Long, List<String>>> map = Maps.newHashMap();

The thing is, I don't understand how it's possible that the newHashMap method can return a Map<String,Map<Long, List<String>>>. How did they write this code? When did it became possible? I lived under the impression that you need to explicitly declare the generics parameters in the constructor call.

+1  A: 

Look at the source. All this method does is provides a nice shortcut:

public static <K, V> HashMap<K, V> newHashMap() {
  return new HashMap<K, V>();
}

This will work for any arguments because HashMap itself is generic.

ChssPly76
True, it provides a shortcut. But why isn't it `Map<whatever> map = Maps.newHashMap<whatever>`?
Geo
@Geo - I don't understand what you mean by your comment
ChssPly76
Say, this example: `ArrayList<String> list = new ArrayList<String>()`. Is it legal to write `ArrayList<String> list = new ArrayList()`? I remember needing to explicitly mention the parameter on both sides of the equal sign.
Geo
@Geo That declaration is different from invoking a method with generic parameters.
Kevin
It's legal but will generate an unchecked warning. The difference between this and above is that above uses a generic method to instantiate your class. In your case the method would be `public static <T> ArrayList<T> newArrayList() { return new ArrayList<T>(); }` and you'd be able to write `List<String> myList = newArrayList();`
ChssPly76
Literal generic method call is: `Maps.<Whatever>newHashMap()`.
Tom Hawtin - tackline
@Tom - yes, but that's only needed when compiler can't figure out what the parameterized types should be.
ChssPly76
+6  A: 

You can declare generic parameters on a method call, not just a whole class. If you look at the source for that method:

public static <K, V> HashMap<K, V> newHashMap() {
    return new HashMap<K, V>();
}

You'll see it declares two generic parameters, K and V and declares the result to be a Map. The compiler is smart enough to figure out K and V from the LHS expression. In your case, K is String and V is Map<Long, List<String>>

Kevin
Is this "figuring out" something new? Or has it been here all the time?
Geo
+1 for saying what I meant in better English
ChssPly76
@Geo Its "new" as of Java 5. I'm not sure what you mean otherwise.
Kevin
I always instantiated the generic classes like: `Whatever<WhateverType> var = new Whatever<WhateverType>()`. I wasn't aware that `Whatever<WhateverType> var = new Whatever()` was legal.
Geo
@Geo That is a "literal" declaration. The results are different when you declare a variable to be the result of a method invocation.
Kevin
@Geo As an aside, your syntax *should* be legal in Java 7 with the new "Type Inference" JSR: http://www.javac.info/Inference.html
Kevin
It seems I have some more reading to do on generics. Thanks guys!
Geo
JDK 7 looks as if it will require a "diamond" syntax: `Whatever<WhateverType> var = new Whatever<>();`. I don't really understand why that is necessary.
Tom Hawtin - tackline