A Detailed Introduction to Functional Programming in Java

On the basis of taking into account object-oriented features, the Java language opens the door to functional programming for developers through Lambda expressions and method references. Let's do a preliminary exploration.

Delayed execution of Lambda

After the code in some scenarios is executed, the result may not be used, resulting in a waste of performance. Lambda expressions are executed lazily, which can be used as a solution to improve performance.

Logging case of performance waste

 
 
注:日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。

A typical scenario is to conditionally use parameters, for example, after splicing log messages, print out when conditions are met:

 
 
public class Demo01Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, msgA + msgB + msgC);
}
private static void log(int level, String mgs) {
if (level == 1) {
System.out.println(mgs);
}
}
}

There is a problem with this code: no matter whether the level meets the requirements, as the second parameter of the log method, the three strings will be concatenated and passed into the method first, and then the level will be judged. If the level does not meet the requirements, then the string concatenation operation will be done in vain, resulting in a waste of performance.

 
 
备注:SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行 字符串的拼接,而是将字符串的若干部分作
为可变参数传入方法中,仅在日志级别满足要求的情况下才会进 行字符串拼接。例如: LOGGER.debug("变量{}的取值为{}。", "os", "macOS") ,
其中的大括号 {} 为占位 符。如果满足日志级别要求,则会将“os”和“macOS”两个字符串依次拼接到大括号的位置;否则不会进行字 符串拼接。这也是
一种可行解决方案,但Lambda可以做到更好。

Experience the better way of writing Lambda

Using Lambda necessarily requires a functional interface:

 
 
@FunctionalInterface
public interface MessageBuilder {
/**
* 信息生成器
* @return 生成的信息
*/
public abstract String builderMessage();
}

Then modify the log method:

 
 
public class Demo02Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, () -> msgA + msgB + msgC);
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

Comparison before and after transformation:

In this way, only when the level meets the requirements, the three strings will be spliced; otherwise, the three strings will not be spliced.

Proving Lambda Latency

The following code can be verified by the results:

 
 
public class Demo03Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(2, () -> {
System.out.println("Lambada 执行!");
return msgA + msgB + msgC;
});
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

Here we just slightly modify the incoming Lambda when calling the log method,

When the incoming level = 1, the console output:

 
 
Lambada 执行!
Hello World Java

When the incoming level != 1, there is no console output.

As can be seen from the results, Lambda will not execute if the level requirements are not met. So as to achieve the effect of saving performance.

 
 
扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调
用其所在方法是在条件判断之后才执行的。
public class Demo04Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, new MessageBuilder() {
@Override
public String builderMessage() {
System.out.println("Lambada 执行!");
return msgA + msgB + msgC;
}
});
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

Use Lambda as parameter and return value

Regardless of the implementation principle, Lambda expressions in Java can be regarded as a substitute for anonymous inner classes. If the method parameter is a functional interface type, then a Lambda expression can be used instead. Using Lambda expressions as method parameters is actually using functional interfaces as method parameters.

Lambda as parameter

For example, the java.lang.Runnable interface is a functional interface. If there is a startThread method that uses this interface as a parameter, then Lambda can be used to pass parameters. In fact, there is no essential difference between this situation and the constructor parameter of the Thread class as Runnable.

匿名内部类作为参数,创建新的线程并执行:

 
 
public class Demo01Runnable {
public static void main(String[] args) {
startThread(new Runnable() {
@Override
public void run() {
System.out.println("线程任务执行!");
}
});
}
/**
* 创建一个新的线程,赋予任务,然后开启线程
* @param runnable 传入Thread类的接口,实现创建新线程
*/
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}

运行程序,控制台输出:

 
 
线程任务执行!

Lambda作为参数,创建新的线程并执行:

 
 
public class Demo02Runnable {
public static void main(String[] args) {
startThread(
() -> System.out.println("线程任务执行!")
);
}
/**
* 创建一个新的线程,赋予任务,然后开启线程
* @param runnable 传入Thread类的接口,实现创建新线程
*/
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}

运行程序,控制台输出:

 
 
线程任务执行!

Lambda作为返回值

类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。

Lambda作为返回值,字符串的长短比较:

 
 
import java.util.Arrays;
import java.util.Comparator;
public class DemoComparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "a" };
System.out.println("使用比较器比较之前:" + Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println("使用比较器比较之后:" + Arrays.toString(array));
}
/**
* 字符串a、b的长短比较,自己定义比较器规则,生序排序,字符串长的排在后面。
* @return 布尔值,
* a.length() - b.length() < 0 返回 false,
* a.length() - b.length() > 0 返回 true,
* a.length() = b.length() 返回 0
*/
public static Comparator<String> newComparator() {
return (a, b) -> a.length() - b.length();
}
}

匿名内部类作为返回值,字符串的长短比较:

 
 
import java.util.Arrays;
import java.util.Comparator;
public class DemoComparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "a" };
System.out.println("使用比较器比较之前:" + Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println("使用比较器比较之后:" + Arrays.toString(array));
}
public static Comparator<String> newComparator1() {
return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};
}
}

运行程序,控制台输出一样:

 
 
使用比较器比较之前:[abc, ab, a]
使用比较器比较之后:[a, ab, abc]

 

Guess you like

Origin blog.csdn.net/Xuange_Aha/article/details/130661244