Lambda表达式
文章目录
- Lambda表达式
- 1、函数式接口
- 2、方法引用
- 3、构造器引用
- 4、数组引用
1、函数式接口
1.
函数式接口当接口只有一个抽象方法时才能实现。
- 必须只有一个抽象方法才能使用,需要注意的是如果接口中有Object类中的抽象方法,那么这个抽象方法不算该接口本身的方法。
- 比如Comparator接口中虽然含有compare和equals两个抽象方法,但equals方法在Object类中是默认实现的,所以Comparator接口还是会满足函数式接口的使用条件。
2.
Lambda表达式是返回一个某个实现了接口方法的对象,其中包含了方法体的具体实现方式,可以通过返回的对象直接调用接口中的抽象方法,如:
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
27import org.junit.Test; public class Java8Lambda01 { @Test public void Test02(){ /* * Lambda表达式做法 * -> 是Lambda表达式的操作符 * 箭头左边是接口抽象方法的参数列表,run方法没有形参,就用一个()代替 * 箭头右边是在run方法中实现的内容,这里是打印一句话 */ Runnable runnable = () -> System.out.println("你目前看过的番中感触最深的是?"); runnable.run(); } @Test public void Test01(){ //这里演示一下普通做法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("我们无法一起学习"); } }; runnable.run(); } }
可以看出Lambda函数式接口的功能强大之处,正常情况下,是需要实现类去实现接口中的抽象方法,然后才能调用该方法。但上例中却直接由一个MathOperation接口的引用调用了该接口的抽象方法,这是很简便的。
3.
Lambda表达式会根据上下文推断出应该返回什么对象,如:
1
2
3
4
5
6
7
8
9
10public void LambdaTest2{ public static void main(String[] args){ /* *这里构造一个匿名对象,Thread构造器中需要传递一个Runnable类型的参数 *此时Lambda表达式根据上下文知道这里是返回一个Runnable实现类的对象 */ new Thread( () -> System.out.println("Hello!") ).start(); } }
4.
Java内置的4大核心函数式接口
接口 | 消费型接口:Consumer | 函数型接口:Function<T,R> | 断定型接口:Predicate | 供给型接口:Supplier |
---|---|---|---|---|
方法 | void accept(T t) | R apply(T t) | boolean test(T t) | T get() |
这些接口都是功能性接口,使用了 @FunctionalInterface标注。了解这些接口,以后如果有需求时就可以直接应用函数式接口,不必再定义自己的接口。此外在java.util.function 包下还有其他大量的功能性接口。
2、方法引用
方法引用也是 JDK 8增加的功能,属于Lambda表达式的变式,主要有3种方式:
1
2
3
4Object::instanceMethod //对象::实例方法 Class::staticMethod //类名::静态方法 Class::instanceMethod //类名::实例方法(难!与众不同)
可以看到方法引用是通过 :: 来分隔类名(或对象名)与方法名的。具体用法如下:
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
27import org.junit.Test; import java.util.function.Consumer; public class Java8Lambda02 { @Test public void Test03(){ /* *本例中使用的方法: *Consumer ---> void accept(T t) *PrintStream ---> void println(T t) */ /*Lambda表达式*/ Consumer<String> consumer = str -> System.out.println(str); consumer.accept("看过很多云"); /* *方法引用---对象::实例方法 *要求:接口方法、类实例方法的返回类型和参数列表要相同 *注意:System.out返回的是PrintStream对象!!! *本例可以写成:Consumer<String> consumer = System.out::println; */ PrintStream ps = System.out; Consumer<String> consumer = ps::println; consumer.accept("却只爱过你"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21@Test public void Test04(){ /* *本例中使用的方法: *Function ---> R apply(T t) *Math ---> Long round(Double d) */ /*Lambda表达式*/ Function<Double, Long> fun1 = aDouble -> Math.round(aDouble); System.out.println(fun1.apply(4.5)); /* *方法引用---类::静态方法 *要求:接口方法、类静态方法的返回类型和参数列表要相同 *本例中泛型自己设置成相同的类型和参数 */ Function<Double, Long> fun2 = Math :: round; System.out.println(fun2.apply(4.5)); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Test public void test06(){ /* *使用的方法: * Comparator ---> int compare(T t1, T t2) * String ---> int t1.compareTo(t2) */ /*Lambda表达式*/ Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc", "abd")); /* * 方法引用---类::实例方法 * 要求:返回类型相同,接口方法参数列表中的第一个变量,作为类实例方法的调用者 */ Comparator<String> com2 = String::compareTo; System.out.println(com2.compare("abc", "abd")); }
第三种方法引用比较难理解,再看一个例子:
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@Test public void test07() { /* *使用的方法: *Predicate ---> boolean test(T t) *String ---> boolean s.isEmpty() */ /*匿名内部类形式*/ Predicate<String> pre = new Predicate<String>() { @Override public boolean test(String s) { return s.isEmpty(); } }; System.out.println(pre.test("123")); /*Lambda表达式*/ Predicate<String> pre1 = s1 -> s1.isEmpty(); System.out.println(pre1.test("123")); /*方法引用---类::实例方法*/ Predicate<String> pre2 = String::isEmpty; System.out.println(pre2.test("123")); }
从第四4个例子也可以看出,方法引用像是Lambda表达式的变形,而Lambda表达式又是匿名内部类的简化,使用Lambda表达式可以让我们编写的代码更加简洁。
3、构造器引用
构造器引用与方法引用差不多,也是属于Lambda表达式的语法糖。通过 类名::new 来实现。
先构造一个示例类Teacher:
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
36public class Teacher { static int i = 1; //方便实现编号自动+1 private int id; private String name; public Teacher(){ id = -1; name = null; System.out.println("无参构造器被调用啦!!!"); } public Teacher(String name){ this.id = i++; this.name = name; } @Override public String toString() { return id + " " + name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
下面来看构造器引用与匿名内部类、Lambda表达式的区别
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
27public class NewTest { @Test public void test1(){ /* *需要用到的接口方法 * Supplier ---> T get() */ /*方式1:匿名内部类*/ Supplier<Teacher> sup1 = new Supplier<Teacher>() { @Override public Teacher get() { return new Teacher(); //返回无参构造器 } }; sup1.get(); /*方式2:Lambda表达式*/ Supplier<Teacher> sup2 = () -> new Teacher(); sup2.get(); /*方法3:构造器引用*/ Supplier<Teacher> sup3 = Teacher::new; sup3.get(); } }
上例调用的是无参构造器,再来看看如何调用含参的构造器:
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@Test public void test2(){ /* *需要用到的接口方法 *Function<T,R> ---> R apply(T t) */ /*方式1:匿名内部类*/ Function<String, Teacher> fun1 = new Function<String, Teacher>() { @Override public Teacher apply(String s) { return new Teacher(s); } }; Teacher tc1 = fun1.apply("黎老师"); System.out.println(tc1); /*方式2:Lambda表达式*/ Function<String, Teacher> fun2 = s1 -> new Teacher(s1); Teacher tc2 = fun2.apply("许老师"); System.out.println(tc2); /*方式3:构造器引用*/ Function<String, Teacher> fun3 = Teacher::new; Teacher tc3 = fun3.apply("欧老师"); System.out.println(tc3); }
从上面两个例子中可以看到,实现构造器引用需要:
- 找到一个有返回类型的泛型接口(只含一个抽象方法),保证返回的类型能设置成相应的类名;
- 接口方法的参数有多少个,就能调用相同参数个数的构造器。如上两例中 get() 调用无参,apply(T t) 调用只含一个参数的构造器,它们都有返回值,返回类型为Teacher。
4、数组引用
数组引用与含一个参数的构造器引用几乎一样,传入数组的长度,返回相应数组的类型,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@Test public void Test3(){ /*匿名内部类*/ Function<Integer, Teacher[]> fun1 = new Function<Integer, Teacher[]>() { @Override public Teacher[] apply(Integer integer) { return new Teacher[integer]; } }; Teacher[] teachers1 = fun1.apply(3); System.out.println(Arrays.toString(teachers1)); /*Lambda表达式*/ Function<Integer, Teacher[]> fun2 = integer -> new Teacher[integer]; Teacher[] teachers2 = fun1.apply(6); System.out.println(Arrays.toString(teachers2)); /*数组引用*/ Function<Integer, Teacher[]> fun3 = Teacher[]::new; Teacher[] teachers3 = fun1.apply(9); System.out.println(Arrays.toString(teachers3)); }
上面例子都采取不断简化的语法,先用常见的匿名内部类,再到Lambda表达式,再到各种引用。这样做是方便对比,可以看出要使用Lambda表达和方法引用、构造器引用、数组引用,都要了解接口抽象方法的参数列表和返回类型 。 这些接口除了能有和Object类相同的方法外,只含一个抽象方法(如Comparator接口中的equals方法,虽然在接口中也是抽象方法,但要排除在外),但可以含有其他默认方法和静态方法。
这章知识点我看书和视频好久,但一直都感觉没掌握好,只能先行将已经学会的知识点记下,以后有机会再仔细看看核心技术卷1的内容,希望各位大佬对笔记中有错的部分多加指点,不胜感激!!
最后
以上就是友好芹菜最近收集整理的关于Lambda表达式的全部内容,更多相关Lambda表达式内容请搜索靠谱客的其他文章。
发表评论 取消回复