有且仅有一个函数接口(可以有其他非抽象的方法),函数式接口的典型使用场景就是作为方法的入参。
比如 java.lang.Runnable
就是一个函数式接口。java.util.function
包中提供了丰富的函数式接口:Supplier、Consummer、Function
等。
用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则会将会报错。与@Override
注解类似。
示例:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
public class DemoMyFunctionalInterface {
// 自定义的函数式接口作为方法的参数
private static void doSomething(MyFunctionalInterface inter) {
inter.myMethod(); // 调用自定义的函数式接口方法
}
public static void main(String[] args) {
// 通过匿名内部类操作 使用了调用函数式接口的方法
doSomething(new MyFunctionalInterface() {
@Override
public void myMethod() {
System.out.println("匿名内部类 执行");
}
});
// 通过lambda操作 使用了调用函数式接口的方法
doSomething(() -> System.out.println("lambda 执行"));
}
}
3.1 Lambda表达式, 也可以称为闭包,它是推动 java 8
发布的最重要的新特性。Lambda
允许把函数作为一个方法的参数(即就是,函数作为参数传递进方法中)。Lambda
表达式的一个重要的用法就是简化某些匿名内部类的写法。
Lambda
标准格式:(参数类型 参数名)->{代码块儿/代码语句}
3.2 省略规则:
在Lambda
标准格式的基础上,使用省略写法的规则:
(1)小括号内参数的类型可以可以省略;
(2)如果小括号内有且仅有一个参数,则小括号可以省略;
(3)如果打括号内有且仅有一个语句,则无论是否返回值,都可以省略大括号、return
关键字以及语句分号。
示例:
@FunctionalInterface
public interface MessageBuilder {
String buildMessage();
}
public class DemoMessageBuilderLogger {
private static void traditionalLog(int level, String msg) {
if (level == 1) {
System.out.println(msg);
}
}
private static void lambdaLog(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";
traditionalLog(1, msgA + msgB + msgC);
lambdaLog(1, new MessageBuilder() {
@Override
public String buildMessage() {
return msgA + msgB + msgC;
}
});
lambdaLog(1,() -> msgA + msgB + msgC);
}
注意:
使用Lambda
的依据必须有相应的函数接口(函数接口,是指内部只有一个抽象方法的接口),Lambda
的实际类型就是对应函数类型的接口类型。
具体内部怎么调用与实现的呢?
JVM
内部就是通过invokedynamic
指令来实现Lambda
表达式的的hi用Lambda
简化过的匿名内部类在外部类编译后不会生成内部类的class
文件,Lambda
表达式被封装成了主类的一个私有方法。并通过invokedynamic
指令进行调用。
lambda是延迟执行的,有些场景的代码执行后,执行结果不一定会被使用,从而造成性能的浪费。而lambda 表达式是延迟执行的,正好可以作为解决方案,提升性能。
这里的Stream
到底是什么呢?
说到Stream
我们很容易想到I/O Stream
,然而实际上没有人规定“流”
就一定是“io流”
,所以在java 8
中得益于Lambda
所带来的函数式编程,引入了一个全新的Stream
概念,用于解决已有集合类库存在的弊端。
我们知道集合中要进行频繁的遍历操作,然而我们经常使用的都是循环遍历,每次需要遍历的操作都需要从 for
从前到后遍历集合,比较麻烦。
我们可以大胆进行对比各种遍历的代码量:
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
/**
* 普通的遍历
*/
System.out.println("普通遍历:");
for (int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
System.out.println("---------------------");
/**
* 增强for循环
*/
System.out.println("增强for遍历:");
for (String s : list){
System.out.println(s);
}
System.out.println("******************");
/**
* 使用迭代器Iterator进行遍历
*/
System.out.println("利用Iterator遍历:");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String s = iterator.next();
System.out.println(s);
}
/**
* 使用Stream 进行遍历
*/
System.out.println("使用Stream遍历:");
list.stream().forEach(System.out::println);
输出结果:
普通遍历:
张无忌
周芷若
赵敏
张强
张三丰
增强for遍历:
张无忌
周芷若
赵敏
张强
张三丰
利用Iterator遍历:
张无忌
周芷若
赵敏
张强
张三丰
使用Stream遍历:
张无忌
周芷若
赵敏
张强
张三丰
到这我们差不多认识了这家伙,后期继续会深入的进行了解并在开发中进行大量的使用。