jdk8-1-函数式接口&lambda表达式

1 lmabda表达式

首先看两段对比代码:
为jbutton 添加一个时间监听器

1
2
3
4
5
6
7
8
9
10
// 老写法: 匿名内部类
jButton.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello Button");
}
});
// lambda 表达式写法
jButton.addActionListener(e -> System.out.println("Hello Button"));

对比两个写法,lambda表达式的写法会简单很多,如果你有多行处理逻辑:

1
2
3
4
5
// 加个大括号
jButton.addActionListener(e ->{
System.out.println("Hello Button!!!!");
System.out.println("Hello Button2!!!!");
});

这里的e,没有声明类型,并不是表示java 不是一个静态语言了,而是,编译器的类型推断告诉可以推断出来这个 e 的类型肯定是一个 AbstractAction, 所以无需去写,当然如写上也没有问题,需要加上括号

1
2
3
4
jButton.addActionListener((AbstractAction e) ->{
System.out.println("Hello Button!!!!");
System.out.println("Hello Button2!!!!");
})

所以 lambda 表达式的基本结构如下(根据单个参数和单个操作可以有一定的省略)

1
2
3
4
(event e1, event e2 ...) -> {
action1;
action2;
}

一开始可能不是特别好理解,我们可以结合 java8 新增的另一个特性 函数式接口 来一起理解为何我们可以如此编写代码

2 函数式接口

什么叫函数式接口呢? 函数式接口是 jdk1.8 中新增的一个接口类型,为此还新增了一个注解 @FunctionalInterface 用于标注函数接口;我们通过阅读 @FunctionalInterfacejdk 文档来进行概念认知。总结起来:

  • 当一个接口 有且只有一个抽象方法,并且这个抽象方法不是继承自 Object 的方法,就会被当做一个 函数式接口
  • 当一个接口被 @FunctionalInterface 注解时,可以当做一个函数式接口
  • 当一个接口被 @FunctionalInterface 注解,但不满足第一个条件的时候,编译器会报错。
  • 当一个接口 没有被 @FunctionalInterface 注解标注,但是满足函数式接口的条件,也会被当做函数式接口

这里的第一条后半部分很重要,如果一个接口,有两个抽象方法,但是其中一个是继承自 Object 类(因为其实万类师祖), 他仍然是一个函数式接口。

例如以下实例就是一个完整的函数式接口,即使他有2个抽象方法,但是toString是继承自 Object 的方法,所以不会算作函数式接口的抽象方法,所以其中的 test() 方法, 是函数式接口认定的唯一的抽象方法。
同理,如果下面的例子只有 toString() 方法,而没有 test() 方法,同时又有 @FunctionalInterface 注解的话,那么编译器会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@FunctionalInterface
interface MyInterface {
/**
* 函数式接口,只能有唯一的抽象方法
*/
void test();
/**
* 因为改抽象方法,继承自 Object,所以不算函数式接口的抽象方法
* 故而,接口可以算作一个函数式接口
*/
String toString();
}

我们可以看一下 @FunctionalInterface 的jdk文档,里面有一句:

image_1b4cpml3c13mn1cju14uurjun9v9.png-24.9kB

意思就是说,函数式接口的实现,可以通过 lambda 表达式 方法引用 以及 构造方法引用 的方式来实现。其实我们上面的 lambda 表达式的例子,就是实现的函数式接口。我们来新写一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test2 {
public void testInterface(MyInterface myInterface) {
System.out.println("start ------------------");
myInterface.test();
System.out.println("end --------------------");
}
public static void main(String[] args) {
Test2 test2 = new Test2();
/*
* 因为函数式接口的里唯一的抽象方法test里面,没有参数
* 但是()不能省略
* 参考Test1 程序里面的Jframe 里的ActionListener
*/
test2.testInterface(() -> {
System.out.println("函数式接口接口实现");
});
}
}

我们可以看到,testInterface 方法需要传递一个 testInterface
的接口,而我们使用的方式就是 lambda 表达式来实现了一个函数式接口。 可以运用下面的形式来理解。

(函数式接口唯一的抽象方法的参数列表) -> {唯一抽象函数的实现}

这个时候,我们看一下最开始的那个给 button 加 action 的例子。
其实实现就是 ActionListener 这个函数式接口的唯一的抽象方法:
image_1b4cq5u2j1548e4eif0l1t1t48m.png-35.9kB
虽然该类没有 @FunctionalInterface 注解,但是满足条件,只有一个抽象方法,所以他也是一个函数式接口,所以我们可以去使用 lambda 表达式去实现他。

朱老师&敏哥 wechat
有惊喜,朋友🙄
我要拿铁不加糖.