Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is the generic type getting inferred here?

Tags:

java

generics

public static void main(String[] args) {
    Map<String, Map<Long, List<String>>> map = getHashMap();
}

static <K,V> Map<K,V> getHashMap()
{
    return new HashMap<K, V>();
}

I saw a similar code in google guava (as factory methods) for making instances of Hashmap without mentioning the generic types.I don't understand how the generic is getting inferred by the above program.I mean how can the function getHashMap understand the type of map since i'm not passing any type information to the function.

like image 418
Emil Avatar asked Sep 21 '10 12:09

Emil


People also ask

What is type inference in generics?

Type Inference in generics provides java compiler the ability to look at method invocation and method declaration in order to determine the type of generic method parameters. Type Inference can also infer return type of generic method if type parameter is also used in return type. The type will be inferred such that method call is valid.

Is it possible to infer only one generic type?

1 You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred. Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

Why do we use generics in programming?

As you might see, generics can really improve type declarations, as sometimes you don’t know the type of a function or variable in a declaration. By using generics we can extract the type and provide better typing when we pass it along.

What is type-type inference in Java?

Type Inference was introduced in Java 5 to complement the introduction of generics and was substantially expanded in following Java releases, which is also referred to as Generalized Target-Type Inference. In this tutorial, we'll explore this concept with code samples.


3 Answers

The getHashMap function does not have to infer the types. It's at the call site that javac is required by the Java Language Spec to infer that the types agree (15.12.2.7 Inferring Type Arguments Based on Actual Arguments).

I believe the current plan is (still) for JDK7 to support the diamond operator, so this sort of thing will work with new too, although with a bit of apparently pointless syntax.

Map<String, Map<Long, List<String>>> map = new HashMap<>();
                                                      ^^diamond
like image 119
Tom Hawtin - tackline Avatar answered Oct 22 '22 22:10

Tom Hawtin - tackline


At the bytecode level the method will have a descriptor that just says, there's a method with the name getHashMap, that takes no arguments and returns a Map (no generics).

Then when the compiler is analyzing the line Map<String, Map<Long, List<String>>> map = getHashMap();, and it will say, ok, I need to have a variable with a declared type of Map<String, Map<Long, List<String>>>, but to actually get the instance I need to call a method. At this point it's the compiler's job to check if the return type of the method matches the declared type of the variable your assigning that result to. So it checks if String matches K, and if Map<Long, List<String>> matches V, which they do, so it considers that the assignment is type safe, and generates the bytecode which basically uses a Map (no generics) variable.

If you would have declared your method as:

static <K extends Number,V> Map<K,V> getHashMap()
{
    return new HashMap<K, V>();
}

when analyzing the assignment, the compiler would see that String doesn't match K extends Number, would throw a compilation error, and won't create bytecode for that assignment.

like image 39
Andrei Fierbinteanu Avatar answered Oct 22 '22 21:10

Andrei Fierbinteanu


class XX
  static <T> T foo(){ return null; }

String  s = XX.foo();
Integer i = XX.foo();

Java infers that T is String in the 1st case, Integer in the 2nd case.

What does "infer" mean? It means that Java guesses that in the two statements, the programmer most like wants T==String in the 1st case, T==Integer for the 2nd case, being a nice guy, Java takes these guesses as fact, and programmers do not have to manually specify the Ts

    String  s = XX.<String> foo();
    Integer i = XX.<Integer>foo();

But really, Java dictates that the return type T must be determined like that.

I find this thing very fishy, not sure what's the reason behind the design. Probably, when (if) Java adds reifiable types(i.e. the real class of T is available at runtime), the design makes more sense:

class XX
  static <T> T foo(){ return new T(); }

String  s = XX.foo();
Integer i = XX.foo();

I still don't like the fact that the type of a method is context dependent.

like image 1
irreputable Avatar answered Oct 22 '22 22:10

irreputable