You don't know the Java Series A Type inference

Source: Internet
Author: User

Allow me to write the story background of this article before I start the formal lecture. A few days ago our production under a tool suddenly inexplicable error, that part of the function has been a long time has not changed, supposedly should not appear problem, code in Do reflection call method when there is classcastexception. I thought it might be a small problem on the task to my colleague, he analyzed down to tell me do not know what the problem, inexplicably suddenly thrown out of the abnormal; then we can only blame Java compiler. We did a JDK upgrade recently, from 7 to 8, At first thought is reflect's method class has changed, the result compares after the same, two eyes a smear, finished .... Well, I'll reveal it at the end of the puzzle.

Knowledge lets you deduce the right thing to do; Expertise makes the right thing a reflex.
-"Unix Programming Art"

The most important thing for a programmer is thinking, knowing its reason.

The following goes into today's topic Type inference.

One, Type inference

What is type inference, the official definition is:
Type inference is a Java compiler ' s ability-look at each method invocation and corresponding declaration to determine T He type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type, the result is being Assigne D, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.

The effect is:
Type inference is a Java compiler that looks at each method invocation and the corresponding declaration to determine the type parameter (or parameter) so that the call can be implemented properly. The inference algorithm determines the type of the parameter, and if the type inference succeeds, then the value returned by the method is that type. Finally, the inference algorithm tries to find the most specific type of work with all the variables.

How to understand this passage, we first split this paragraph into several concepts:

    • Generic Method-generic methods.
    • Type Parameters-Types parameter, which is generic type Parameter
    • Methods invocation-method invocation, type inference occurs primarily at the time of a method call.
    • Target type-Destination types
    • Inference algorithm-type inference algorithm, the following example is used to illustrate how this type of projection works.

Two, Generic Method

If you want to make the type inference clear or to start with the generic method, what is the generic method? Look at the following example
The Util class includes a generic method, compare, which compares, Pair objects:

public class Util {public static <k, v> Boolean compare (Pair<k, v> p1, pair<k, v> p2) {    Retu RN P1.getkey (). Equals (P2.getkey ()) &&        p1.getvalue (). Equals (P2.getvalue ());}} public class pair<k, v> {    private K key;    Private V value; public Pair (K key, V value) {This.key = key; this.value = value;} public void Setkey (K key) {This.key = key;} public void SetValue (V value) {this.value = value,} public K GetKey () {return key;} Public V GetValue () {return value;}}            

This is a classic generic method, which is called the Compare method, which accepts 2 pair types with a type parameter of K and V (this is not a generic type). Generic Method is composed of several parts:
1. Type Parameter, bracket-Enclosed section
2. An identical <type parameter> appears before the return type, and if it is a static method then this <type parameter> must be present.
3. A return type, which can be the type parameter corresponds to, or not.

So exactly how to determine the type of K and V, this type to be determined at the time of the method call, this is the type inference.

Three, Type inference instance commentary

Suppose I have a generic method, T Here is the type argument, this method takes a parameter of 2 T type, returns the result of a type T.
1.

Static <T> T pick (t A1, T A2) {return a2;}

Now go to call this method,

Serializable s = pick ("D", New Arraylist<string> ());

Let's split it up:
1. The first A1 parameter is passed in "D", and the type is string.
2. The second A2 parameter is passed into the arraylist<string> the type is ArrayList.
3. String and ArrayList are implementations of the interface serializable, so the return value of pick method is infers to the serializable type.

2. Take another look at an example of generic method

public classBoxdemo {public static <U> voidAddbox (U u, java.util.list<box<u>>boxes)        {box<u> Box = new box<>();        Box.set (U);    Boxes.add (box); } public static <U> void Outputboxes (java.util.list<box<u>> boxes) {int counter = 0; For (box<u> box:boxes) {U boxcontents = box.get (); System.out.println ("Box #" + Counter + "contains [" + boxcontents.tostring () + "]"); counter++;}} public static void main (string[] args) {java.util.arraylist<box<integer>> listofintegerboxes = new Java . util. Arraylist<>(); Boxdemo.<integer>addbox (integer.valueof (Ten), listofintegerboxes); Boxdemo.addbox (Integer.valueof (), listofintegerboxes); Boxdemo.addbox (Integer.valueof (), listofintegerboxes); Boxdemo.outputboxes (listofintegerboxes); }}

The following is the output from this example:

Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
Addbox is a generic method accepts a U type parameter, of course, this method is to accept 2 parameters, the first is the U type parameter, the 2nd is the U type of box type list, you can see in the main method we use 2 ways to invoke Addbox, The first is to show the Java compiler I want to use the integer type, the second is type inference, I do not display the specified I want to use integer, Java compile based on the type of parameters passed in to infer which type of method should be used.

Four, target type

Java Compiler infers (infers) which type of result is returned based on the target type you specify, for example:

Collections.emptyliststatic <T> list<t> emptylist (); This method has no parameters, only one type of return of type T, then I can't pass in the parameter how does this method know what the return type is, which is the target type
Listone is a variable of type list<string>, and Java compiler infers from this target type that Emptylist method should return this type, which is dependent on the assignment context, That is, which variable I want to assign, and what type it is, and what type I will return.


Let's consider one more case, and now I have a way to accept a list<string> parameter.

void Processstringlist (list<string> stringlist) {//Process stringlist}//is now called: processstringlist ( Collections.emptylist ()); 

This is not compiled in Java 7 because Java 7 does not support method type inference, the T type defaults to object, and then a compilation error occurs
List<object> cannot is converted to list<string>.

Five, Context

The above refers to the method type inference, what is the method type inference, it is necessary to say the concept of the context, Java compiler when doing type inference mainly depends on the context. There are currently 2 kinds of context.

    • Assignment Context
    • Method Context

Assignment contextual is the assignment context, and it is well understood that the type of the generic method is inferred from the left side of the assignment statement.

The method context, as the name implies, is not as straightforward as assignment, and JDK 7 has no method infers this thing. The method context infers the type of the incoming call method based on the parameter type of the method that accepts the parameter.

The above example is in JDK 8 and it makes sense.

void Processstringlist (list<string> stringlist) {//Process stringlist}//is now called: processstringlist ( Collections.emptylist ()); 

JDK 8 introduces method infers, which means that Java compiler determines what type of t type it should be based on the context of the current method, which is the string type.

When it comes to Java 8, you have to say lambda.

VI, LAMBDA & Stream

Wait, this isn't about the type inference, so why do you say lambda? One of the core concepts that lambda is important is type inference.
Let's look at one of the following:

predicate<integer> predicate = (var), var > 0; P.s. At first glance, it looks cool.

To say that Java lambda that is to say functional interface, functional interface is only an abstract method interface, such interface can be called functional interface.
So what does this lambda expression have to do with type inference, first let's take a look at the method declaration of the predicate interface
Boolean Test (T T);
The reason why the above lambda expression succeeds is because of the definition of this method, accepting a parameter of type T, returning a Boolean value, which involves a function descriptor. You will have the opportunity to do a single phase of lambda, and then look at the above assignment statement, see (Var), var > 0 don't know what type of Var, when this expression is assigned to Perdicate<integer> This var is an integer type inferred from the assignment context type.
All pre-defined functional interface under the Java.util.function package are all dependent on the type inference.
Here's an example of stream:

list<string> threehighcaloricdishnames =menu.stream (). Filter (D-d.getcalories () >). Map ( dish::getname). Limit (3). Collect (Collectors.tolist ());  


The filter method accepts a perdicate<t> parameter, the map method accepts a function<t, r> parameter, collect accepts a parameter of type collect, This collect is constructed by collectors, the utility class, and if you look at the source code of collectors, you will find that almost all methods use generic method. All of this is derived from the type inference of method context.
If you do not infer the method context type of Java 8, you will not be able to use this chain structure at all, and you will not be able to write such concise code.

Seven, legacy issues

Finally, the question left at the beginning, I thought I had a bug in JDK 8 before I was sure it was a type inference problem, and someone did get the same problem and reported a bug in OpenJDK [url]https://bugs.openjdk.java.net/ Browse/jdk-8072919[/url], but the problem was resolve and said it wasn't a bug, okay, I admit it's not really a bug.

The problem is that there are now 2 ways to do the following:

 1//method 1:2 void  invoke (Object obj, Object ... objs) 3//Accept 2 parameters an Object, a object[], is actually to reflect Method.invoke 4//Method 2:5 <t>  T ReadValue () {6 list<string> List = new Arraylist<>  (); 7  list.add ("Te St "); 8 return  (T) list; 9 }10//Call the Invoke method with 2 methods, one//1.12 invoke ("1231 ", ReadValue ());//2.14 List<stri ng> list = ReadValue (); Invoke ("12312", list);        

Let's analyze 2 different ways to call:
1. The first method directly passes the return value of ReadValue () as a parameter to the Invoke method, which requires the method context to infers the ReadValue return type, and the second parameter of the Invoke method is Object ... That is object[], then through infers to determine the type of ReadValue is object[], oooooops, this code will throw a classcastexception, because ArrayList can not cast to Object[], Java.utils.ArrayList cannot is cast to [Ljava.lang.Object;
2. The second method can be executed correctly, because we first call the ReadValue method and assign it to list variable, then we have the target type, and Java compiler returns list<string> by infers decision. Value, and then passing the list to the Invoke method is no problem.
3. Note: These 2 different invocation methods are successful at JDK 7 because JDK 7 does not have type inference for method context, so T is considered an object, so there is no problem with the ReadValue internal type conversion. Because all classes inherit object.

To get a more intuitive look at what the JVM did, I wrote a simple little example, and then we looked at what the Java Compiler did to the class bytecode.
e.g. 1:

1     static void            } 4  5 public     static void         }10 one static <T> T Gen () {list<t> list = new arraylist<>();//add list Item14 return (T) list;15}

This is the code that will appear classcastexception
Java.lang.ClassCastException:java.util.ArrayList cannot is cast to [Ljava.lang.Object;

e.g. 2: This is the code that can be executed:

1     static void            } 4  5 public     static void main (string[] args) {6         list<string > list = }10 one static <T> T Gen () {list<t> list = new arraylist<>();//add list Item14 return (T) list;15}     

These 2 different invocation methods in this bytecode is very clear, the 2nd call method produces a type list of the local variable, and is a type parameter is a string list, referring to Astore_1 iconst_1, aload_1 instructions.


sum up: The article is written well, summary is very important

1. Type inference is kind inference and infers the specific type based on the context of the currently calling method.
2. If method has a parameter of type T, then the type of T is determined by the type of the passed-in parameter.
3. If method does not have a type parameter but has a return of type T, consider Context,target type, which is assignment context or method context.
4. JDK 8 introduces the concept of method context to implement method infers type parameters. Functional interface and Stream APIs use method type inference extensively. (If you're interested, I'll do a separate article about Lambda and stream.)

Hopefully I can explain it well enough to help you understand the type inference.

You don't know the Java Series A Type inference

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.