Introduction to the

Lambda expressions The functional programming framework introduced in Java 8. We also covered the basic usage of Lambda expressions in previous articles.

This article will be based on the previous article to explain in more detail the best practices in the actual use of Lambda expressions.

The standard Functional interface is preferred

As we discussed in previous articles, Java defines a number of function interfaces in the java.util.function package. Pretty much every genre we can think of.

Suppose we customize the following Functional Interface:

@FunctionalInterface
public interface Usage {
    String method(String string);
}
Copy the code

Then we need to pass the interface in a test method:

public String test(String string, Usage usage) {
    return usage.method(string);
}
Copy the code

The function interface we defined above needs to implement the method method, which takes a String and returns a String. We could have used Function instead:

public String test(String string, Function<String, String> fn) {
    return fn.apply(string);
}
Copy the code

The advantage of using a standard interface is that you don’t have to reinvent the wheel.

Use @functionalInterface annotations

Although @functional Interface is not required, you can define a FunctionalInterface without @FunctionalInterface.

However, using @FunctionalInterface can raise an alarm if the FunctionalInterface definition is violated.

If you’re maintaining a large project, the @FunctionalInterface annotation makes it clear to others what this class does.

This makes the code more formal and usable.

So we need to define it like this:

@FunctionalInterface
public interface Usage {
    String method(String string);
}
Copy the code

Instead of:

public interface Usage {
    String method(String string);
}
Copy the code

Do not abuse Default Methods in Functional Interfaces

Functional Interface is an Interface that has only one unimplemented abstract method.

If you have more than one method in the Interface, you can provide a default implementation for it using the default keyword.

But we know that interfaces can be multi-inherited, and a class can implement multiple interfaces. If the same default method is defined on multiple interfaces, an error is reported.

Generally speaking, the default keyword is used in upgrade projects to avoid code errors.

Use Lambda expressions to instantiate the Functional Interface

Again, the above example:

@FunctionalInterface
public interface Usage {
    String method(String string);
}
Copy the code

To instantiate Usage, we can use the new keyword:

Usage usage = new Usage() {
    @Override
    public String method(String string) {
        returnstring; }};Copy the code

But the best way to do this is to use lambda expressions:

Usage usage = parameter -> parameter;
Copy the code

Do not override methods that use Functional Interface as arguments

How do you understand that? Let’s look at the following two methods:

public class ProcessorImpl implements Processor {
    @Override
    public String process(Callable<String> c) throws Exception {
        // implementation details
    }
 
    @Override
    public String process(Supplier<String> s) {
        // implementation details}}Copy the code

The names of the methods are the same, except for the arguments passed in. But both parameters are Functional interfaces and can be represented with the same lambda expression.

When called:

String result = processor.process(() -> "test");
Copy the code

An error is reported because you cannot tell which method is called.

The best thing to do is to change the names of the two methods to be different.

Lambda expressions are different from inner classes

Although we talked earlier about using lambda expressions to replace inner classes. But the scope of the two is different.

In the inner class, a new scope is created. Within the scope, you can define new variables and refer to them as this.

In Lambda expressions, however, no new scope is defined; if this is used in Lambda expressions, it refers to an external class.

Let’s take an example:

private String value = "Outer scope value";

public String scopeExperiment(a) {
    Usage usage = new Usage() {
        String value = "Inner class value";
 
        @Override
        public String method(String string) {
            return this.value; }}; String result = usage.method("");
 
    Usage usageLambda = parameter -> {
        String value = "Lambda value";
        return this.value;
    };
    String resultLambda = usageLambda.method("");
 
    return "Results: result = " + result + 
      ", resultLambda = " + resultLambda;
}

Copy the code

Results: result = Inner class value, resultLambda = Outer scope value

Lambda Expression is as concise as possible

One line of code is usually enough. If you have a lot of logic, you can encapsulate it into a method that you can call in a lambda expression.

Because a lambda expression is ultimately an expression, the shorter the expression, the better.

Java uses type inference to determine the type of arguments passed in, so we try not to pass the type of arguments in lambda expressions, as follows:

(a, b) -> a.toLowerCase() + b.toLowerCase();
Copy the code

Instead of:

(String a, String b) -> a.toLowerCase() + b.toLowerCase();
Copy the code

If there is only one argument, no parentheses are required:

a -> a.toLowerCase();
Copy the code

Instead of:

(a) -> a.toLowerCase();
Copy the code

Return values do not need a return:

a -> a.toLowerCase();
Copy the code

Instead of:

a -> {return a.toLowerCase()};
Copy the code

Use method reference

To make lambda expressions more concise, we can use method references when we can use them:

a -> a.toLowerCase();
Copy the code

Can be replaced with:

String::toLowerCase;
Copy the code

Effectively Final variables

If a non-final variable is referenced ina lambda expression, an error is reported.

Well, what does that mean, effectively final? This is an approximation of final. As long as a variable is assigned only once, the compiler will treat it as effectively final.

    String localVariable = "Local";
    Usage usage = parameter -> {
         localVariable = parameter;
        return localVariable;
    };
Copy the code

In the example above, localVariable is assigned twice, so it is not a properly Final variable and will compile an error.

Why do you do that? Because lambda expressions are often used in parallel computations, Effectively Final variables can prevent unexpected changes when variables are accessed by multiple threads simultaneously.

conclusion

Lambda is a very useful function, I hope you can master it in your work.

Welcome to pay attention to my public number: procedures those things, more wonderful waiting for you! For more, visit www.flydean.com