函数式接口
函数式接口:有且只有一个抽象方法的接口,称之为函数式接口。当然接口中可以包含其他的方法(默认,静态,私有)。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。
从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。
@FunctionalInterface注解
作用:可以检测接口是否是一个函数式接口
是:编译成功
否:编译失败(接口中没有抽象方法、抽象方法的个数多于1个)
例如:
@FunctionalInterface
public interface MyFunctionalInterface {
public abstract void method();
}
【函数式接口的使用】:一般可以作为方法的参数和返回值类型
// 定义一个方法,参数使用函数式接口MyFunctionalInterface
public static void show(MyFunctionalInterface myInter){
myInter.method();
}
public static void main(String[] args) {
// 调用show方法,方法的参数是一个接口,所以可以传递接口的实现类对象
show(new MyFunctionalInterfaceImpl());
// 调用show方法,方法的参数是一个接口,所以我们可以传递接口的匿名内部类
show(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("使用Lambda表达式重新接口中的抽象方法");
}
});
// 调用show方法,方法的参数是一个函数式接口,所以我们可以使用Lambda表达式
show(()->{
System.out.println("使用Lambda表达式重写接口中的抽象方法");
});
// 简化Lambda表达式
show(()->System.out.println("使用Lambda表达式重写接口中的抽象方法"));
}
性能浪费的日志案例
日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。
public static void showLog(int level, String message){
// 对日志的等级进行判断,如果是1级别,那么输出日志信息
if(level == 1){
System.out.println(message);
}
}
public static void main(String[] args){
// 定义三个日志信息
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
// 调用showLog方法,传递日志级别和日志信息
showLog(1,msg1 + msg2 + msg3);
}
注意:
发现代码存在一些性能浪费的问题。调用showLog方法,传递的第二个参数是一个拼接后的字符串。先把字符串拼接好,然后再调用showLog方法。
showLog方法中如果传递的日志等级不是1级,那么就不会输出拼接后的字符串。所以感觉字符串就白拼接了,存在了浪费。
使用Lambda优化日志案例
Lambda的特点:延迟加载
Lambda的使用前提,必须存在函数式接口
@FunctionalInterface
public interface MessageBuilder {
// 定义一个拼接消息的抽象方法,返回被拼接的消息
public abstract String builderMessage();
}
public static void showLog(int level, MessageBuilder mb){
// 对日志的等级进行判断,如果是1级,则调用MessageBuilder接口中的builderMessage方法
if(level == 1){
System.out.println(mb.builderMessage());
}
}
public static void main(String[] args) {
String msg1 = "Hello";
String msg2 = "World";
String msg3 = "Java";
// 调用showLog方法,参数MessageBuilder是一个函数式接口,所以可以传递Lambda表达式
showLog(1,()->{
// 返回一个拼接好的字符串
return msg1 + msg2 + msg3;
});
}
注意:
使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中。
只有满足条件,日志的等级是1级。才会调用接口MessageBuilder中的方法builderMessage,才会进行字符串的拼接。
如果条件不满足,日志的等级不是1级。那么MessageBuilder接口中的方法builderMessage也不会执行,所以拼接字符串的代码也不会执行。不会存在性能的浪费。
函数式接口作为方法的参数案例
java.lang.Runnable接口就是一个函数式接口
假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参
这种情况其实和Thread类的构造方法参数为Runnable没有本质区别
// 定义一个方法startThread,方法的参数使用函数式接口Runnable
public static void startThread(Runnable run){
// 开启多线程
new Thread(run).start();
}
public static void main(String[] args){
// 调用startThread方法,方法的参数是一个接口,那么我们可以传递这个接口的匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + "线程启动了");
}
});
// 调用startThread方法,方法的参数是一个函数式接口,所以可以传递Lambda表达式
startThread(()->{
System.out.println(Thread.currentThread().getName() + "-->" + "线程启动了");
});
// 优化Lambda表达式
startThread(()->System.out.println(Thread.currentThread().getName() + "-->" + "线程启动了"));
}
函数式接口作为方法的返回值类型案例
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式
当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调用该方法获取。
// 定义一个方法,方法的返回值类型使用函数式接口Comparator
public static Comparator<String> getComparator(){
// 方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
/*return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 按照字符串的降序排序
return o2.length() - o1.length();
}
};*/
// 方法的返回值类型是一个函数式接口,所以我们可以返回一个Lambda表达式
/*return (String o1, String o2)->{
return o2.length() - o1.length();
};*/
// 继续优化Lambda表达式
return (o1, o2)->o2.length() - o1.length();
}
public static void main(String[] args) {
// 创建一个字符串数组
String[] arr = {"aaa","b","cccccc","ddddddddddd"};
// 输出排序前的数组
System.out.println(Arrays.toString(arr)); // [aaa, b, cccccc, ddddddddddd]
// 调用Arrays中的方法,对字符串数组进行排序
Arrays.sort(arr,getComparator());
// 输出排序后的数组
System.out.println(Arrays.toString(arr)); // [ddddddddddd, cccccc, aaa, b]
}