Type inference and lambda expressions in Java

Posted May 26, 20204 min read


Java is a strongly typed programming language. Every variable used in Java needs to define its type, otherwise it will fail to compile. The advantage of a strongly typed language is that it can find possible problems in the code during compilation as much as possible, thereby reducing the possibility of problems at runtime.

In contrast, the disadvantage of a strongly typed language is that it is not so flexible, and it is redundant to write.

Before JDK8, Java did not support type inference. In JDK8, lambda expressions were introduced, and type inference was generated from this.

This article will explain the best practices of type inference in lambda expressions and the things to pay attention to when using them.

More exciting content and see:

For more information, please visit www.flydean.com

Type of display use

Suppose we define a CustUser class, and there are two attributes of age and name:

public class CustUser {
    int age;
    String name;

Let's see how we can display the usage type in Stream:

public static void testStream() {
        Stream.of(new CustUser(10, "alice"), new CustUser(20, "bluce"))
                .forEach((CustUser custUser)-> System.out.println(custUser.name));

In the above example, we constructed a CustUser type Stream and processed CustUser in the forEach method.

forEach receives a Consumer object, and Consumer needs to implement the void accept(T t) method. Because of the Consumer function interface, we can use lambda expressions instead.

Here, we show that a CustUser type is passed in. There is no problem with the code compilation, but it looks a bit more complicated. Next we look at how to use type inference in Stream.

Type inference in Stream

The above example, we can rewrite it like this:

public static void testInference() {
        Stream.of(new CustUser(10, "alice"), new CustUser(20, "bluce"))
                .forEach(custUser-> System.out.println(custUser.name));

Here we have not defined the type of custUser, but java can be inferred from the type in Stream. So there is no problem to write this way, you can compile normally.

The importance of variable names in type inference

In the above example, we defined the name of the variable as custUser, and people who viewed the code can see at a glance that this parameter represents the custUser parameter of the CustUser type.

Writing meaningful names can greatly improve the readability and maintainability of the code. If you write like this:

forEach(u-> System.out.println(u.name)

Although the code has become shorter, it loses its readable meaning. At first glance, everyone does not know what u stands for, which affects the readability of the code.

Therefore, the definition of variable names must be meaningful.

The impact of type inference on performance

Type inference is a good thing, so some students will ask, will type inference affect the performance of java?

We can divide java into two parts:compile and run. Type inference is something that is done during compilation. Using type inference may increase the time for code compilation, but it has no effect on runtime efficiency.

In general, we are concerned about the performance of the program at runtime rather than compile time, so type inference has no effect on performance.

Type inference limitations

Although Java has type inference, this inference has certain limitations. It cannot think like humans, but it is already smart enough. Here we give an example:

public static Comparator <CustUser> createUser1() {
        return(CustUser user1, CustUser user2)-> user1.getAge()-user2.getAge();

In the above example, we need to create a Comparator, using lambda expression we can generate a Comparator.

Comparator needs to implement the method int compare(T o1, T o2), passing in two parameters and returning an int.

In the above example, we show that the type of the two specified parameters is CustUser, and there is no problem with compilation.

Can I specify the CustUser type if it is not displayed?

public static Comparator <CustUser> createUser2() {
        return(user1, user2)-> user1.getAge()-user2.getAge();

The answer is also ok. In this example, we did not pass in user1, user2, how does java find the type of user1 and user2?

Note that in the above example, we defined the return type to be CustUser. Java uses this return type to infer that the actual type passed in is CustUser. Is it very smart?

If we split the return statement above into two, will there be a problem?

Comparator comparator =(user1, user2)-> user1.getAge()-user2.getAge();

At this time, the compiler will report an error saying that the getAge method cannot be found. This is because the Comparator we returned did not indicate the type, so it is of type Object by default. The Object type does not have a getAge method, so an error is reported.

We can rewrite it like this:

Comparator <CustUser> comparator =(user1, user2)-> user1.getAge()-user2.getAge();

Compilation is complete without errors.

to sum up

In addition to the type inference used in the lambda representation introduced in JDK8, in fact, the type inference of var local variables in JDK10 is also used. For details, please refer to [New Features of JDK10:Local Variable Type Var]( http://www.flydean . com/jdk10-var-local-variable /).

Example of this article \ [ https://github.com/ddean2009/
learn-java-base-9-to-20 ]([ https://github.com/ddean2009/...] ( https://github.com/ddean2009/learn-java-base-9-to- 20))

Author:flydean program those things

Link to this article: http://www.flydean.com/java-type-inference-lambda/

Source of this article:flydean's blog

Welcome to pay attention to my public number:those things of the program, more exciting waiting for you!