Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How JDK 8's type inference works with generic?

I have code which is failed to compile with JDK 7 but succeed to compile with JDK 8.

To abstract actual code:

interface A {
...
}

class B implements A {
...
}

public void AAA(List<A> list) {...}

AAA(Collections.singletonList(new B()));

Collections.singletonList is defined as

public static <T> List<T> singletonList(T o) {
    return new SingletonList<>(o);
}

So as far as I know based on generic, T will be inferred to B, so Collections.singletonList(new B()) will be List which cannot be assigned to List since Java generic is invariant.

But with JDK 8, T is inferred to A and compilation succeeds.

I'd like to know how T is inferred to A, since there're two variables here for type T: A and B.

Is there order of priority? Or does compiler find common ancestor class?

Attaching official document is more appreciated!

Thanks in advance!

ps1. The version of JDK 7 is Oracle 1.7.0_79 and the version of JDK 8 is Oracle 1.8.0_66.

ps2. Here's the links for actual code:

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/examples/storm-opentsdb-examples/src/main/java/org/apache/storm/opentsdb/SampleOpenTsdbBoltTopology.java#L48

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/external/storm-opentsdb/src/main/java/org/apache/storm/opentsdb/bolt/OpenTsdbBolt.java#L77

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/external/storm-opentsdb/src/main/java/org/apache/storm/opentsdb/bolt/TupleOpenTsdbDatapointMapper.java#L37

like image 670
Jungtaek Lim Avatar asked Jan 19 '17 14:01

Jungtaek Lim


People also ask

How does Java type inference work?

Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.

What is infer generic type arguments?

Type inference represents the Java compiler's ability to look at a method invocation and its corresponding declaration to check and determine the type argument(s). The inference algorithm checks the types of the arguments and, if available, assigned type is returned.

How does type inference work?

Type inference is the ability to automatically deduce, either partially or fully, the type of an expression at compile time. The compiler is often able to infer the type of a variable or the type signature of a function, without explicit type annotations having been given.

Does Java use type inference?

Type inference is a feature of Java which provides ability to compiler to look at each method invocation and corresponding declaration to determine the type of arguments. Java provides improved version of type inference in Java 8.


2 Answers

Well, there is an entire new chapter, §18. Type Inference, in the language specification, but it’s not an easy read. Even the first section’s summary, addressing exactly your question, is tough:

In comparison to the Java SE 7 Edition of The Java® Language Specification, important changes to inference include:

  • Adding support for lambda expressions and method references as method invocation arguments.
  • Generalizing to define inference in terms of poly expressions, which may not have well-defined types until after inference is complete. This has the notable effect of improving inference for nested generic method and diamond constructor invocations.
  • Describing how inference is used to handle wildcard-parameterized functional interface target types and most specific method analysis.
  • Clarifying the distinction between invocation applicability testing (which involves only the invocation arguments) and invocation type inference (which incorporates a target type).
  • Delaying resolution of all inference variables, even those with lower bounds, until invocation type inference, in order to get better results.
  • Improving inference behavior for interdependent (or self-dependent) variables.
  • Eliminating bugs and potential sources of confusion. This revision more carefully and precisely handles the distinction between specific conversion contexts and subtyping, and describes reduction by paralleling the corresponding non-inference relations. Where there are intentional departures from the non-inference relations, these are explicitly identified as such.
  • Laying a foundation for future evolution: enhancements to or new applications of inference will be easier to integrate into the specification.

The second bullet has the biggest impact on your code example. You have a nested method invocation of a generic method without specifying explicit type arguments, which makes it a so-called poly expression whose actual type might be inferred from the target type, which is the parameter type of AAA in your case.

So this is a rather easy constellation as AAA isn’t generic and no ambiguity regarding it’s parameter type. It’s always List<A>. There is no search for a “common ancestor class” here, all that has to be checked, is whether the argument expression’s type (B) is compatible with the inferred type (A).

like image 127
Holger Avatar answered Oct 06 '22 01:10

Holger


This is actually explained here at the end at Target Types.

Target Type has been expanded to include method argument ...

What this means is that

 AAA(Collections.singletonList(new B())); // returns List<A> NOT List<B>

In jdk-7 method argument is not used to find out the target type, that is why T is inferred to B and that is why it fails.

like image 44
Eugene Avatar answered Oct 06 '22 01:10

Eugene