Lambda expression best practices
brief introduction
Lambda expression is a functional programming framework introduced by Java 8. In previous articles, we also talked about the basic usage of lambda expressions.
Based on the previous articles, this paper will explain the best practice experience of lambda expression in practical application in more detail.
Standard functional interfaces are preferred
As we mentioned in the previous article, Java is in Java util. Many function interfaces are defined in the function package. It basically covers all types we can think of.
Suppose we customize the following functional interface:
@FunctionalInterface
public interface Usage {
String method(String string);
}
Then we need to pass in the interface in a test method:
public String test(String string,Usage usage) {
return usage.method(string);
}
The function interface defined above needs to implement the method method, receive a string and return a string. In this way, we can use function instead of:
public String test(String string,Function<String,String> fn) {
return fn.apply(string);
}
The advantage of using a standard interface is that you don't build wheels again.
Use @ functionalinterface annotation
Although @ functional interface is not required, you can define a functional interface without @ functional interface.
However, using @ functional interface can alarm when the definition of functional interface is violated.
If you are maintaining a large project, adding the @ functionalinterface annotation can clearly let others understand the role of this class.
This makes the code more standardized and more usable.
So we need to define it this way:
@FunctionalInterface
public interface Usage {
String method(String string);
}
instead of:
public interface Usage {
String method(String string);
}
Don't abuse default methods in functional interfaces
A functional interface is an interface with only one abstract method that is not implemented.
If there are multiple methods in the interface, you can use the default keyword to provide a default implementation for it.
However, we know that interfaces can be inherited, and one class can implement multiple interfaces. If the same default method is defined in multiple interfaces, an error will be reported.
Generally speaking, the default keyword is generally used in upgrading projects to avoid code errors.
Use lambda expressions to instantiate functional interfaces
Or the above example:
@FunctionalInterface
public interface Usage {
String method(String string);
}
To instantiate usage, we can use the new keyword:
Usage usage = new Usage() {
@Override
public String method(String string) {
return string;
}
};
But the best way is to use a lambda expression:
Usage usage = parameter -> parameter;
Do not override methods with functional interface as parameters
How to understand? 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
}
}
The method names of the two methods are the same, only the parameters passed in are different. However, both parameters are functional interfaces, which can be expressed by the same lambda expression.
When calling:
String result = processor.process(() -> "test");
Because you can't tell which method to call, an error will be reported.
The best way is to change the names of the two methods to different ones.
Lambda expressions are different from inner classes
Although we mentioned earlier that using lambda expressions can replace inner classes. However, the scope of the two is different.
In the inner class, a new scope will be created. Within this scope, you can define a new variable and reference it with this.
However, there is no new scope defined in the lambda expression. If this is used in the lambda expression, it points to an external class.
Let's take an example:
private String value = "Outer scope value";
public String scopeExperiment() {
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;
}
The above example will output "results: result = inner class value, resultlambda = outer scope value"
Lambda expression is as concise as possible
Usually one line of code is enough. If you have a lot of logic, you can encapsulate these logic into one method and call this method in lambda expression.
Because lambda expression is still an expression after all, the shorter the expression, the better.
Java judges the passed in parameter type through type inference, so we try not to pass the parameter type in the parameters of lambda expression, as follows:
(a,b) -> a.toLowerCase() + b.toLowerCase();
instead of:
(String a,String b) -> a.toLowerCase() + b.toLowerCase();
If there is only one parameter, parentheses are not required:
a -> a.toLowerCase();
instead of:
(a) -> a.toLowerCase();
Return value does not need return:
a -> a.toLowerCase();
instead of:
a -> {return a.toLowerCase()};
Use method reference
To make lambda expressions more concise, when method references can be used, we can use method references:
a -> a.toLowerCase();
Can be replaced by:
String::toLowerCase;
Effectively final variable
If a non final variable is referenced in a lambda expression, an error will be reported.
What does "effectively final" mean? This is an approximate final meaning. As long as a variable is assigned only once, the compiler will treat the variable as effectively final.
String localVariable = "Local";
Usage usage = parameter -> {
localVariable = parameter;
return localVariable;
};
In the above example, localvariable is assigned twice, so it is not an effectively final variable, and an error will be reported during compilation.
Why set it like this? Because lambda expressions are usually used in parallel computing, when multiple threads access variables at the same time, the effectively final variable can prevent unexpected modifications.
conclusion
Lambda is a very useful function. I hope my friends can master it at work.