在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。下面我们做一个初探。
一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:
public class Demo01Logger {
private static void log(int level, String msg) {
if (level == 1) {
System.out.println(msg);
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(1, msgA + msgB + msgC);
}
}
这段代码存在问题:无论级别是否满足要求,作为log
方法的第二个参数,三个字符串一定会首先被拼接并传入log方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费
@FunctionalInterface
public interface MessageBuilder {
String buildMessage(String... msgs);//可变参数
}
然后对log方法进行改造:
public class Demo02LoggerLambda {
private static void log(int level, MessageBuilder builder) {
if (level == 1) {
System.out.println(builder.buildMessage());
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(1, () -> msgA + msgB + msgC );
}
}
说明:
由于上述使用了Lambda表达式,Lambda表达式是在执行代码:builder.buildMessage() 时才会执行msgA + msgB + msgC。
而builder.buildMessage() 能够执行的前提是level等于1.如果level不是1,那么就不会执行builder.buildMessage(),这样一来msgA + msgB + msgC也就不会执行了。
public class Demo03LoggerDelay {
private static void log(int level, MessageBuilder builder) {
if (level == 1) {
System.out.println(builder.buildMessage());
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(2, () -> {
System.out.println("Lambda执行!");
return msgA + msgB + msgC;
});
}
}
从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。
扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。只不过使用new会在堆中开辟一个对象空间,这样也会浪费空间。
如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的语法糖。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
例如java.lang.Runnable
接口就是一个函数式接口,假设有一个startThread
方法使用该接口作为参数,那么就可以使用Lambda进行传参。
public class Demo04Runnable {
public static void main(String[] args) {
startThread(()-> System.out.println("线程任务执行"));
}
//自定义方法
private static void startThread(Runnable task) { //函数式接口作为参数,task是一个任务类
new Thread(task).start(); //使用匿名函数创建线程,将任务类作为参数传入,并启动线程。
}
}
使用Lambda来取代内部类有诸多好处,不仅语义更加简洁明确,而且可以减少独立的.class字节码文件的相关操作从而节省性能。
自定义Lambda参数
函数式接口MySupplier
如:
@FunctionalInterface
//自定义一个函数式接口,抽象方法名为get,返回值类型为Object类型。
public interface MySupplier {
Object get();
}
public class Demo05Supplier {
public static void main(String[] args) {
printParam(()->"Hello"); //在传递参数时将实际参数写成Lambda
}
private static void printParam(MySupplier supplier) { //使用函数式接口作为参数
System.out.println(supplier.get());
}
}
定义一个字符串数组按照字符串的长度降序排序
//需求:定义一个字符串数组按照字符串的长度降序排序。
public class Demo {
public static void main(String[] args) {
//定义一个字符串数组
String[] arr={"abc","ab","abcds","adef"};
//自然排序
// Arrays.sort(arr);
// //输出
// System.out.println(Arrays.toString(arr));//[ab, abc, abcds, adef]
//使用自定义比较器Comparator按照字符串长度进行排序
/*Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();//因为是降序,所以是o2在前面
}
});*/
//使用Lambda完成上述功能
/*Arrays.sort(arr,(o1,o2)->{
return o2.length() - o1.length();//[abcds, adef, abc, ab]
});*/
//简化版
// Arrays.sort(arr,(o1,o2)-> o2.length() - o1.length());//[abcds, adef, abc, ab]
Arrays.sort(arr,getComparator());//[abcds, adef, abc, ab]
System.out.println(Arrays.toString(arr));//[abcds, adef, abc, ab]
}
//定义一个方法来提供自定义比较器Comparator的对象
public static Comparator<String> getComparator()
{
//返回Comparator的对象
/*return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();//因为是降序,所以是o2在前面
}
};*/
//使用Lambda表达式返回Comparator的对象
return (o1,o2)-> o2.length() - o1.length();
}
}
------------------------------------------------------
运行结果:
[abcds, adef, abc, ab]
其中直接return一个Lambda表达式即可
public class Demo07MySupplier {
private static MySupplier getData() {
return () -> "Hello";
}
private static void printData(MySupplier supplier) {
System.out.println(supplier.get());
}
public static void main(String[] args) {
printData(getData()); //其中main方法不再自己指定Lambda表达式,而是通过调用一个getData方法来获取Lambda的内容。
}
}
其中main方法不再自己指定Lambda表达式,而是通过调用一个getData方法来获取Lambda的内容。
对于Lambda的总结:
其实可以理解为1.8之后Lambda的诞生的思想是write less do more(写的少,做的多)。间接可以将Lambda理解为是对匿名内部类的简化,Lambda可以更节省空间和代码量并完成相同的功能。