Lambda、函数式接口、引用表达式
写在前面
最近在学习Stream的api,发现Java推出的函数式接口、Lambda表达式、引用表达式等大都服务于Stream的api使用,但其实并不一定,所以收集整理了下关于相关特性的用法。
在这只会讲解下基本的用法,关于函数编程框架的详细解读,大家可以参考下这篇 Java8 函数式编程探秘,介绍的非常全面。
函数式接口
先讲一个注解 @FunctionalInterface
1 |
|
@FunctionalInterface 注解要求接口有且只有一个抽象方法,JDK中有许多类用到该注解,比如 Runnable,它只有一个 Run 方法。(注:默认方法并不是抽象方法)
1 |
|
Java8中接口可以定义静态方法,直接通过类名调用,并且接口中可以通过default为抽象方法提供默认的实现。接口继承接口时,如果默认方法相同,则需要进行重写
Java8推出的Lambda表达式只能针对函数式接口使用。
Lambda的语法
首先Lambda可以认为是一种特殊的匿名内部类,其次lambda只能用于函数式接口。
lambda语法:
1 | ([形参列表,不带数据类型])-> { |
要注意的点:
- 如果形参列表是空的,只需要保留()即可
- 如果没有返回值。只需要在{}写执行语句即可
- 如果接口的抽象方法只有一个形参,()可以省略,只需要参数的名称即可
- 如果执行语句只有一行,可以省略{},但是如果有返回值时,情况特殊。
- 如果函数式接口的方法有返回值,必须给定返回值,如果执行语句只有一句,还可以简写,即省去大括号和return以及最后的;号。
- 形参列表的数据类型会自动推断,只需要参数名称。
eg:
1 | public class TestLambda { |
1 |
|
可以看出,lambda表达式和匿名内部类并不完全相同
观察生成的class文件可以看出,lambda表达式并不会生成额外的.class文件,而匿名内部类会生成Test$1.class1
2
3
4cn.lb.Test$1@4554617c
使用匿名内部类实现
cn.lb.Test$$Lambda$1/1324119927@404b9385
使用lambda表达式实现
函数式接口引用表达式
- 引用实例方法:
自动把调用方法的时候的参数,全部传给引用的方法
1 | <函数式接口> <变量名> = <实例> :: <实例方法名> |
引用类方法(静态方法):
自动把调用方法的时候的参数,全部传给引用的方法引用类的实例方法:
定义、调用接口方法的时候需要多一个参数,并且参数的类型必须和引用实例方法的类型必须一致,把第一个参数作为引用的实例,后面的每个参数全部传递给引用的方法。
1 | interface <函数式接口> { |
eg:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52public class TestMethodRef {
public static void main(String[] args) {
MethodRef r1 = (s) -> System.out.println(s);
r1.test("普通方式");
//使用方法的引用:实例方法的引用
//System.out是一个实例 out是PrintStream 类型,有println方法
MethodRef r2 = System.out::println;
r2.test("方法引用");
//MethodRef1 r3 =(a)-> Arrays.sort(a);
//引用类方法
MethodRef1 r3 = Arrays::sort;
int[] a = new int[]{4, 12, 23, 1, 3};
r3.test(a);
//将排序后的数组输出
r1.test(Arrays.toString(a));
//引用类的实例方法
MethodRef2 r4 = PrintStream::println;
//第二个之后的参数作为引用方法的参数
r4.test(System.out, "第二个参数");
//引用构造器
MethodRef3 r5 = String::new;
String test = r5.test(new char[]{'测', '试', '构', '造', '器', '引', '用'});
System.out.println(test);
//普通情况
MethodRef3 r6 = (c) -> {
return new String(c);
};
String test2 = r6.test(new char[]{'测', '试', '构', '造', '器', '引', '用'});
System.out.println(test2);
}
}
interface MethodRef {
void test(String s);
}
interface MethodRef1 {
void test(int[] arr);
}
interface MethodRef2 {
void test(PrintStream out, String str);
}
//测试构造器引用
interface MethodRef3 {
String test(char[] chars);
}