1.Lambda表达式与函数式接口
通过网盘分享的文件:webflux
链接: https://pan.baidu.com/s/1IHo8Kv3uSI8qscmOuLFQHw?pwd=j456 提取码: j456
–来自百度网盘超级会员v6的分享
一、Lambad表达式与函数式接口
主要内容
-
Lambda表达式的概述
-
Lambda表达式的使用
-
Lambda表达式的方法引用
-
Lambda表达式的在集合中的使用
1、Lambda表达式的概述
1.1.1、Lambda表达式的引入
Lambda表达式是JDK1.8的一个新特性,可以取代大部分的匿名内部类以便写出更优雅的Java代码,尤其在集合的遍历和其他集合操作中可以极大地优化代码结构
1.1.1.1、传统接口实现方式
(1)、自己编写实现类
如果想要实现一个接口的某个方法,就需要自己定义一个实现类重写接口中的方法来实现接口中方法的功能
如果想要实现接口中同一方法的不同处理逻辑,就需要写不同的实现类来完成相同的功能
【示例】
如下示例想要实现接口中sum(int a, int b)方法,一种实现逻辑为对两个参数相加,另一种实现逻辑为对参数a乘10后再与b相加,就需要编写两个实现类完成
package com.longdidi.lambda;
interface MyInterface1 {
int sum(int a, int b);
}
/*自己写实现类方式实现逻辑1*/
class MyInterface1Impl1 implements MyInterface1 {
@Override
public int sum(int a, int b) {
return a + b;
}
}
/*自己写实现类方式实现逻辑2*/
class MyInterface1Impl2 implements MyInterface1 {
@Override
public int sum(int a, int b) {
return a * 10 + b;
}
}
public class LambdaDemo1 {
/*测试使用Lambda表达式实现逻辑*/
public static void main(String[] args) {
MyInterface1Impl1 myInterface1 = new MyInterface1Impl1();
System.out.println(myInterface1.sum(1, 2));
MyInterface1Impl2 myInterface2 = new MyInterface1Impl2();
System.out.println(myInterface2.sum(1, 2));
}
}

(2)、匿名内部类实现
使用匿名内部类的方式完成上面的案例
package com.longdidi.lambda;
interface MyInterface2 {
int sum(int a, int b);
}
public class LambdaDemo2 {
public static void main(String[] args) {
/*使用匿名内部类实现逻辑1*/
MyInterface2 myInterface1 = new MyInterface2() {
@Override
public int sum(int a, int b) {
return a + b;
}
};
System.out.println(myInterface1.sum(1, 2));
/*使用匿名内部类实现逻辑2*/
MyInterface2 myInterface2 = new MyInterface2() {
@Override
public int sum(int a, int b) {
return a * 10 + b;
}
};
System.out.println(myInterface2.sum(1, 2));
}
}

1.1.1.2、Lambda实现
使用Lambda表达式的代码非常优雅并且还非常的简洁
【示例1】
package com.longdidi.lambda;
interface MyInterface3 {
int sum(int a, int b);
}
public class LambdaDemo3 {
public static void main(String[] args) {
// lambda表达式实现方式:参数列表+箭头+方法体
MyInterface3 myInterface1 = (a, b) -> a + b;
System.out.println(myInterface1.sum(1, 2));
MyInterface3 myInterface2 = (a, b) -> a * 10 + b;
System.out.println(myInterface2.sum(1, 2));
}
}

1.1.2、Lambda和匿名内部类对比
-
所需类型不同
-
匿名内部类
可以是接口、抽象类具体类
-
Lambda表达式
只能是接口
-
-
使用限制不同
- 如果接口中有且仅有一个抽象方法可以使用Lambda表达式也可以使用匿名内部类
- 如果接口中有多个抽象方法则就只能使用匿名内部类而不能使用Lambda表达式
-
实现原理不同
-
匿名内部类
编译之后会生成一个单独的.class字节码文件
-
Lambda表达式
编译之后没有生成一个单独的.class字节码文件
-
【示例】
package com.longdidi.lambda;
/*
* Description:
* Lambda表达式和匿名内部类的区别:
*
* 所需类型不同:
* 匿名内部类,可以是抽象类,也可以是接口。
* Lambda表达式,只能是接口。
*
* 使用限制不同:
* Lambda表达式使用的接口中要求有且只有一个抽象方法。
* 匿名内部类方式使用的接口可以有多个抽象方法。
*
* 实现原理不同:
* 采用匿名内部类的话,编译之后会生成一个.class文件。
* 采用Lambda表达式的话,编译之后不会生成单独的.class文件。
*/
public class LambdaDemo4 {
public static void main(String[] args) {
// 匿名内部类方式(匿名内部类可以是一个抽象类)
LambdaDemo4.test(new Animal() {
@Override
public void run() {
System.out.println("Animal run....");
}
});
// 尝试将上面的代码修改为Lambda表达式方式
// 编译报错,原因是:只有接口才可以使用Lambda表达式
//LambdaDemo4.test(() -> { System.out.println("Animal run...."); });
// 匿名内部类
LambdaDemo4.doFly(new Flyable() {
@Override
public void run() {
System.out.println("run.....");
}
@Override
public void fly() {
System.out.println("fly.....");
}
});
// 尝试使用Lambda表达式
// Lambda表达式使用的接口必须是函数式接口。(必须是接口,而且接口中有且只有一个抽象方法。)
/*LambdaDemo4.doFly(() -> {
System.out.println("run.....");
});*/
}
public static void test(Animal a) {
a.run();
}
public static void doFly(Flyable f) {
f.fly();
f.run();
}
}
abstract class Animal {
public abstract void run();
}
interface Flyable {
void run();
void fly();
}
2、函数式接口
1.2.1、函数式编程思想
Java从诞生之日起就一直倡导"一切皆对象",在Java语言中面向对象(OOP)编程就是一切
但是随着Python和Scala等语言的崛起和新技术的挑战,Java也不得不做出调整以便支持更加广泛的技术要求,即Java语言不但支持OOP还支持OOF(面向函数编程)
JDK1.8引入Lambda表达式之后Java语言也开始支持函数式编程,但是Lambda表达式不是Java语言最早使用的,目前C++、C#、Python、Scala等语言都支持Lambda表示
-
面向对象的思想
做一件事情就找一个能解决这个事情的对象,然后调用对象的方法最终完成事情
-
函数式编程思想
只要能获得结果,谁去做的、怎么做的都不重要,重视的是结果,不重视实现过程
在函数式编程语言中,函数被当成一等公民对待
在将函数当成一等公民的编程语言中,Lambda表达式的类型是函数,但是Lambda表达式却是一个对象,而不是函数,它们必须依附于一类特别的对象类型,也就是所谓的函数式接口
简单点说JDK1.8中的Lambda表达式就是一个函数式接口的实例,这就是Lambda表达式和函数式接口的关系
也就是说只要一个对象是函数式接口的实例,那么该对象就可以使用Lambda表达式来表示
1.2.2、函数式接口
(1)、什么是函数式接口
能够使用Lambda表达式的一个重要依据是必须有相应的函数式接口
所谓的函数式接口指的就是"一个接口中有且只能有一个抽象方法"
也就是说如果一个接口只有一个未实现抽象方法那么该接口就是一个函数式接口
【示例】
package com.longdidi.func;
public interface Flyable1 {
// 在函数式接口中,我们有且只能定义一个抽象方法
void showFly();
// 但是可以定义任意多个默认方法或静态方法
default void show() {
System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法");
}
}
class Flyable1Test {
public static void main(String[] args) {
// 创建接口对象
Flyable1 flyable1 = () -> {
System.out.println("JDK1.8之前,接口只能定义抽象方法");
};
flyable1.showFly();
}
}
(2)、判断函数式接口
如果在接口上声明了"@FunctionalInterface"注解那么编译器就会按照函数式接口的定义来要求该接口,也就是该接口中有且只能定义一个抽象方法
【示例】
package com.longdidi.func;
@FunctionalInterface
public interface Flyable2 {
// 在函数式接口中,我们有且只能定义一个抽象方法
void showFly();
// 但是可以定义任意多个默认方法或静态方法
default void show() {
System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法");
}
}
如果该接口中定义了多个或0个抽象方法,则程序编译时就会报错
【示例】
package com.longdidi.func;
@FunctionalInterface
public interface Flyable3 {
// 在函数式接口中,我们有且只能定义一个抽象方法
void showFly1();
void showFly2();
// 但是可以定义任意多个默认方法或静态方法
default void show() {
System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法");
}
}

从某种意义上来说只要保证接口中有且只有一个抽象方法则接口中没有使用 @FunctionalInterface 注解来标注那么该接口也依旧属于函数式接口
在以下代码中,Flyable4接口中没有使用@FunctionalInterface 注解,但是Flyable4接口中只存在一个抽象方法,因此Flyable4接口依旧属于函数式接口,那么使用Lambda表达式就可以表示Flyable4接口的实例
【示例】
package com.longdidi.func;
public interface Flyable4 {// 在函数式接口中,我们有且只能定义一个抽象方法
void showFly();
}
class Flyable4Test {
public static void main(String[] args) {
// 使用lambda表示来表示Flyable接口的实例
Flyable4 flyable = () -> {
System.out.println("小鸟自由自在的飞翔");
};
// 调用Flyable接口的实例的showFly()方法
flyable.showFly();
}
}

(3)、函数式接口的出入参
-
有入参无出参(消费者):function.accept();
package com.longdidi.func; import java.util.function.BiConsumer; public class Param01 { public static void main(String[] args) { BiConsumer<String, String> function = (a, b) -> { //能接受两个入参 System.out.println("哈哈:" + a + ";呵呵:" + b); }; function.accept("1", "2"); } } -
有入参有出参(多功能函数):function.apply();
package com.longdidi.func; import java.util.function.Function; public class Param02 { public static void main(String[] args) { Function<String, Integer> function = (x) -> Integer.parseInt(x); System.out.println(function.apply("100")); } } -
无入参无出参(普通函数):
package com.longdidi.func; public class Param03 { public static void main(String[] args) { Runnable runnable = () -> System.out.println("aaa"); new Thread(runnable).start(); } } -
无入参有出参(提供者):supplier.get()
package com.longdidi.func; import java.util.function.Supplier; public class Param04 { public static void main(String[] args) { Supplier<String> supplier = () -> "1111"; String s = supplier.get(); System.out.println(s); } }
1.2.3、内置的函数式接口
1.2.3.1、内置的函数式接口
java内置的函数式接口在java.util.function包中

接口说明如下
| 接口名称 | 描述 |
|---|---|
| Consumer | 接受一个参数,无返回值 |
| BiConsumer | 接受两个参数,无返回值 |
| DoubleConsumer | 接受一个double类型的参数,无返回值 |
| IntConsumer | 接受一个int类型的参数,无返回值 |
| LongConsumer | 接受一个long类型的参数,无返回值 |
| ObjDoubleConsumer | 接受一个自定义类型的参数和一个double类型的参数,无返回值 |
| ObjIntConsumer | 接受一个自定义类型的参数和一个int类型的参数,无返回值 |
| ObjLongConsumer | 接受一个自定义类型的参数和一个long类型的参数,无返回值 |
| Function | 接受一个参数,有返回值 |
| BiFunction | 接受两个参数,有返回值 |
| DoubleFunction | 接受一个double类型的参数,有返回值 |
| IntFunction | 接受一个int类型的参数,有返回值 |
| LongFunction | 接受一个long类型的参数,有返回值 |
| IntToDoubleFunction | 接受一个int类型的参数,返回一个double类型的值 |
| IntToLongFunction | 接受一个int类型的参数,返回一个long类型的值 |
| LongToDoubleFunction | 接受一个long类型的参数,返回一个double类型的值 |
| LongToIntFunction | 接受一个long类型的参数,返回一个int类型的值 |
| DoubleToIntFunction | 接受一个double类型的参数,返回一个int类型的值 |
| DoubleToLongFunction | 接受一个double类型的参数,返回一个long类型的值 |
| ToDoubleBiFunction | 接受两个参数,返回一个double类型的值 |
| ToDoubleFunction | 接受一个参数,返回一个double类型的值 |
| ToIntBiFunction | 接受两个参数,返回一个int类型的值 |
| ToIntFunction | 接受一个参数,返回一个int类型的值 |
| ToLongBiFunction | 接受两个参数,返回一个long类型的值 |
| ToLongFunction | 接受一个参数,返回一个long类型的值 |
| BinaryOperator | 接受两个相同类型的参数,返回一个相同类型的值 |
| DoubleBinaryOperator | 接受两个double类型的参数,返回一个double类型的值 |
| DoubleUnaryOperator | 接受一个double类型的参数,返回一个double类型的值 |
| IntBinaryOperator | 接受两个int类型的参数,返回一个int类型的值 |
| IntUnaryOperator | 接受一个int类型的参数,返回一个int类型的值 |
| LongBinaryOperator | 接受两个long类型的参数,返回一个long类型的值 |
| LongUnaryOperator | 接受一个long类型的参数,返回一个long类型的值 |
| UnaryOperator | 接受一个参数,返回一个相同类型的值 |
| Predicate | 接受一个参数,返回一个boolean类型的值 |
| BiPredicate | 接受两个参数,返回一个boolean类型的值 |
| DoublePredicate | 接受一个double类型的参数,返回一个boolean类型的值 |
| IntPredicate | 接受一个int类型的参数,返回一个boolean类型的值 |
| LongPredicate | 接受一个long类型的参数,返回一个boolean类型的值 |
| Supplier | 无参数,有返回值 |
| BooleanSupplier | 无参数,返回一个boolean类型的值 |
| DoubleSupplier | 无参数,返回一个double类型的值 |
| IntSupplier | 无参数,返回一个int类型的值 |
| LongSupplier | 无参数,返回一个long类型的值 |
1.2.3.2、四个基本的函数式接口
(1)、接口说明
通常函数接口出现的地方都可以使用Lambda表达式,所以不必记忆函数接口的名字
| 名字 | 接口名 | 对应的抽象方法 | 含义 |
|---|---|---|---|
| 消费 | Consumer | void accept(T t); | 消费类型表示要消费参数,没有返回值 |
| 生产 | Supplier | T get(); | 生产类型表示要生产数据,因此会提供一个返回值 |
| 转换 | Function<T, R> | R apply(T t); | 转换就是将一个数据转换成另一个数据,因此需要一个参数并将转换后的数据返回 |
| 判断 | Predicate | boolean test(T t); | 判断就是验证提供的数据是否是想要的类型,因此需要一个入参,返回boolean值 |
(2)、简单使用
【示例】
package com.longdidi.func;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunctionTest01 {
public static void main(String[] args) {
// Supplier 生成教据:定义教据提供者函数
Supplier<String> supplier = () -> "42";
// Predicate 验证数据:验证是否一个数字
Predicate<String> isNumeric = str -> str.matches(" ?\\d+(\\.\\d+)?");
// Function 转换数据:把字符电变成教字类::实例方法(静态方法)
Function<String, Integer> parseToInt = Integer::parseInt;
// Consumer 消要数据:打印数字
Consumer<Integer> consumer = num -> {
if (num % 2 == 0) {
System.out.println(num + " is even");
} else {
System.out.println(num + "is odd");
}
};
}
}
(3)、串联使用
串在一起,实现判断42这个字符串是奇数还是偶数
【示例】
package com.longdidi.func;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunctionTest02 {
public static void main(String[] args) {
// Supplier 生成教据:定义教据提供者函数
Supplier<String> supplier = () -> "42";
// Predicate 验证数据:验证是否一个数字
Predicate<String> isNumeric = str -> str.matches(" ?\\d+(\\.\\d+)?");
// Function 转换数据:把字符电变成教字类::实例方法(静态方法)
Function<String, Integer> parseToInt = Integer::parseInt;
// Consumer 消要数据:打印数字
Consumer<Integer> consumer = num -> {
if (num % 2 == 0) {
System.out.println(num + " is even");
} else {
System.out.println(num + "is odd");
}
};
// 串在一起,实现判断42这个字符串是奇数还是偶数
myMethod(supplier, isNumeric, parseToInt, consumer);
}
private static void myMethod(Supplier<String> supplier,
Predicate<String> isNumeric,
Function<String, Integer> parseToInt,
Consumer<Integer> consumer) {
if (isNumeric.test(supplier.get())) {
consumer.accept(parseToInt.apply(supplier.get()));
} else {
System.out.println("不是一个数字");
}
}
}
3、Lambda表达式的使用
1.3.1、Lambda表达式的语法
Lambda表达式本质就是一个匿名函数
在函数的语法中包含返回值类型、方法名、形参列表和方法体等
在Lambda表达式中只需要关心形参列表和方法体即可

在Java语言中Lambda表达式的语法为"(形参列表) -> {方法体}"
-
“->”
lambda操作符或箭头操作符
-
“形参列表”
对应接口实现类中重写方法的形参列表
-
“方法体”
为对应接口实现类中重写方法的方法体
Lambda表达式的精简语法
- 形参类型可以省略,如果需要省略,则每个形参的类型都要省略
- 如果形参列表中只存在一个形参,那么形参类型和小括号都可以省略
- 如果方法体当中只有一行语句,那么方法体的大括号也可以省略
- 如果方法体中只有一条return语句,那么大括号可以省略,且必须去掉return关键字
【示例】
package com.longdidi.lambda;
import java.util.*;
/*
* Lambda表达式的语法格式:
* (形式参数列表) -> {
* 方法体;
* }
*/
public class LambdaDemo5 {
public static void main(String[] args) {
// 1、使用工具类对List集合中的元素排序
List<Integer> list1 = Arrays.asList(100, 200, 300, 250);
Collections.sort(list1);
System.out.println(list1);
System.out.println("================================");
//2、使用比较器方式
List<Integer> list2 = Arrays.asList(100, 200, 300, 250);
Comparator<Integer> comparator = (Integer a, Integer b) -> {
return a - b;
};
Collections.sort(list2, comparator);
System.out.println(list2);
System.out.println("================================");
// 3、这是匿名内部类的方式
List<Integer> list3 = Arrays.asList(100, 200, 300, 250);
Collections.sort(list3, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(list3);
System.out.println("================================");
// 4、改成Lambda表达式的形式
List<Integer> list4 = Arrays.asList(100, 200, 300, 250);
Collections.sort(list4, (Integer a, Integer b) -> {
return a - b;
});
System.out.println(list4);
}
}

Lambda本质上就是去掉了一堆没有意义的代码,只留下核心的代码逻辑,从而让代码看起来更加的简洁且优雅
1.3.2、Lambda表达式的使用
接下来以自定义的函数式接口为例,先从匿名对象的实现过程,慢慢演变为Lambda表达式的实现过程
另外使用Lambda表达式的时候,则必须有上下文环境,才能推导出Lambda对应的接口类型
1.3.2.1、无返回值函数式接口
(1)、无返回值无参数
【示例】
package com.longdidi.lambda;
/**
* Lambda表达式的使用:关于无返回值无参数的函数式接口。
* <p>
*/
public class LambdaDemo6 {
public static void main(String[] args) {
// 匿名内部类方式
NoParameterNoReturn npnr = new NoParameterNoReturn() {
@Override
public void test() {
System.out.println("无返回值无参数的test方法执行了。");
}
};
npnr.test();
// 改成Lambda表达式
NoParameterNoReturn npnr2 = () -> {
System.out.println("无返回值无参数的test方法执行了。");
};
npnr2.test();
// 精简
NoParameterNoReturn npnr3 = () -> System.out.println("无返回值无参数的test方法执行了。");
npnr3.test();
}
}
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
(2)、无返回值一个参数
【示例】
package com.longdidi.lambda;
/**
* Lambda表达式的使用:关于无返回值一个参数的函数式接口。
*/
public class LambdaDemo07 {
public static void main(String[] args) {
// 匿名内部类的方式
OneParameterNoReturn opnr = new OneParameterNoReturn() {
@Override
public void test(Integer value) {
System.out.println("Integer-->" + value);
}
};
opnr.test(1000);
// Lambda表达式方式
OneParameterNoReturn opnr2 = (Integer value) -> {
System.out.println("Integer-->" + value);
};
opnr2.test(2000);
// 精简
OneParameterNoReturn opnr3 = value -> System.out.println("Integer-->" + value);
opnr3.test(2000);
}
}
@FunctionalInterface
interface OneParameterNoReturn {
void test(Integer value);
}
(3)、无返回值多个参数
【示例】
package com.longdidi.lambda;
/**
* Lambda表达式的使用:关于无返回值多个参数的函数式接口。
*/
public class LambdaDemo08 {
public static void main(String[] args) {
// 匿名内部类的方式
MoreParameterNoReturn mpnr = new MoreParameterNoReturn() {
@Override
public void test(Integer value1, Integer value2) {
System.out.println(value1 + value2);
}
};
mpnr.test(100, 200);
// Lambda表达式方式
MoreParameterNoReturn mpnr2 = (Integer value1, Integer value2) -> {
System.out.println(value1 + value2);
};
mpnr2.test(300, 400);
// 精简
MoreParameterNoReturn mpnr3 = (value1, value2) -> System.out.println(value1 + value2);
mpnr3.test(300, 400);
}
}
@FunctionalInterface
interface MoreParameterNoReturn {
void test(Integer value1, Integer value2);
}
1.3.2.2、有返回值函数接口
(1)、有返回值无参数
【示例】
package com.longdidi.lambda;
/*
* Lambda表达式的使用:关于有返回值无参数的函数式接口。
*/
public class LambdaDemo09 {
public static void main(String[] args) {
// 匿名内部类的方式
NoParameterHasReturn nphr = new NoParameterHasReturn() {
@Override
public Integer test() {
return 300;
}
};
System.out.println(nphr.test());
// Lambda表达式的方式
NoParameterHasReturn nphr2 = () -> {
return 500;
};
System.out.println(nphr2.test());
// 精简
NoParameterHasReturn nphr3 = () -> 500;
System.out.println(nphr3.test());
}
}
@FunctionalInterface
interface NoParameterHasReturn {
Integer test();
}
(2)、有返回值一个参数
【示例】
package com.longdidi.lambda;
/*
* Lambda表达式的使用:关于有返回值一个参数的函数式接口。
*/
public class LambdaDemo10 {
public static void main(String[] args) {
// 匿名内部类的方式
OneParameterHasReturn ophr = new OneParameterHasReturn() {
@Override
public Integer test(Integer value) {
return value * 2;
}
};
System.out.println(ophr.test(100));
// Lambda表达式的方式
OneParameterHasReturn ophr2 = (Integer value) -> {
return value * 2;
};
System.out.println(ophr2.test(200));
// 精简
OneParameterHasReturn ophr3 = value -> value * 2;
System.out.println(ophr3.test(200));
}
}
@FunctionalInterface
interface OneParameterHasReturn {
Integer test(Integer value);
}
(3)、有返回值多个参数
【示例】
package com.longdidi.lambda;
/*
* Lambda表达式的使用:关于有返回值多个参数的函数式接口。
*/
public class LambdaDemo11 {
public static void main(String[] args) {
// 匿名内部类方式
MoreParameterHasReturn mphr = new MoreParameterHasReturn() {
@Override
public Integer test(Integer value1, Integer value2) {
return value1 + value2;
}
};
System.out.println(mphr.test(1, 2));
// Lambda表达式的方式
MoreParameterHasReturn mphr2 = (Integer value1, Integer value2) -> {
return value1 + value2;
};
System.out.println(mphr2.test(3, 4));
// 精简
MoreParameterHasReturn mphr3 = (a, b) -> a + b;
System.out.println(mphr3.test(3, 4));
}
}
@FunctionalInterface
interface MoreParameterHasReturn {
Integer test(Integer value1, Integer value2);
}
4、方法引用
1.4.1、方法引用的概述
如果Lambda表达式的方法体中除了调用现有方法之外什么都不做,满足这样的条件就有机会使用方法引用来实现
在以下的代码中,在重写的apply()方法中仅仅只调用了现有Math类round()方法,也就意味着Lambda表达式中仅仅只调用了现有Math类round()方法,那么该Lambda表达式就可以升级为方法引用
【示例】
// 需求:实现小数取整的操作
// 方式一:使用匿名对象来实现
Function<Double, Long> function1 = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(function1.apply(3.14));
// 方式二:使用Lambda表达式来实现
Function<Double, Long> function2 = aDouble -> Math.round(aDouble);
System.out.println(function2.apply(3.14));
// 方式三:使用方法引用来实现
Function<Double, Long> function3 = Math :: round;
System.out.println(function3.apply(3.14));
对于方法引用可以看做是Lambda表达式深层次的表达
换句话说方法引用就是Lambda表达式,也就是函数式接口的一个实例
方法引用中主要有
- 实例方法引用
- 静态方法引用
- 特殊方法引用
- 构造方法引用
- 数组引用
这五种情况
1.4.2、方法引用示例
(1)、实例方法引用
1.语法格式
-
语法
对象 :: 实例方法
-
特点
在Lambda表达式的方法体中通过"对象"来调用指定的某个"实例方法"
-
要求
函数式接口中抽象方法的返回值类型和形参列表 与 内部通过对象调用某个实例方法的返回值类型和形参列表 保持一致

2.使用示例
package com.longdidi.lambda;
import java.util.function.Consumer;
/*
* Description: 实例方法引用
*/
public class LambdaDemo12 {
public static void main(String[] args) {
// 匿名内部类的方式
// 使用消费型的函数式接口
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("龙弟弟...");
// 修改为Lambda表达式
Consumer<String> consumer1 = s -> System.out.println(s);
consumer1.accept("龙弟弟...");
// 使用 实例方法引用 精简
Consumer<String> consumer2 = System.out::println;
consumer2.accept("龙弟弟...");
}
}

(2)、静态方法引用
1.语法格式
-
语法
类 :: 静态方法
-
特点
在Lambda表达式的方法体中通过"类名"来调用指定的某个"静态方法
-
要求
函数式接口中抽象方法的返回值类型和形参列表 与 内部通过类名调用某个静态方法的返回值类型和形参列表保持一致

2.使用示例
package com.longdidi.lambda;
import java.util.function.Function;
/*
* Description: 静态方法引用
*
* <p>
* 语法格式:
* 类名::静态方法名
*
* <p>
* 条件:
* 函数式接口中的方法的 返回值类型 和 形参
* 与
* 内部调用静态方法的 返回值类型 和 形参
* 一致。
*/
public class LambdaDemo13 {
public static void main(String[] args) {
// 匿名内部类方式
// 使用转换型函数式接口
Function<Double, Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double value) {
return Math.round(value);
}
};
System.out.println(function.apply(3.14));
// Lambda表达式
Function<Double, Long> function2 = value -> Math.round(value);
System.out.println(function2.apply(5.67));
// 静态方法引用改进
Function<Double, Long> function3 = Math::round;
System.out.println(function3.apply(5.67));
}
}

(3)、特殊方法引用
1.语法格式
-
语法
类名 :: 实例方法
-
特点
在Lambda表达式的方法体中通过方法的第一个形参来调用指定的某个"实例方法"
-
要求
把函数式接口中抽象方法的第一个形参作为方法的调用者对象,并且从第二个形参开始(或无参)可以对应到被调用实例方法的参数列表中,并且返回值类型保持一致

2.使用示例
【示例1】
package com.longdidi.lambda;
import java.util.Comparator;
/*
* Description: 特殊方法引用
* 语法格式:
* 类名::实例方法
*
* 条件:
* 1. 函数式接口中抽象方法的第一个参数作为内部方法调用对象。
* 2. 从函数式接口的抽象方法的第二参数开始 与 内部调用方法时的参数类型 一致。
* 3. 函数式接口中的抽象方法返回值类型 与 内部方法返回值类型 一致。
*/
public class LambdaDemo14 {
public static void main(String[] args) {
// 匿名内部类
Comparator<Double> comparator = new Comparator<Double>() {
@Override
public int compare(Double o1, Double o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(3.14, 5.6));
// Lambda表达式
Comparator<Double> comparator2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator2.compare(3.14, 5.6));
// 特殊方法引用
Comparator<Double> comparator3 = Double::compareTo;
System.out.println(comparator3.compare(3.14, 5.6));
}
}

【示例2】
package com.longdidi.lambda;
import java.util.function.Function;
/*
* Description: 特殊方法引用
* 语法格式:
* 类名::实例方法名
*/
public class LambdaDemo15 {
public static void main(String[] args) {
// 转换型的函数式接口
// 匿名内部类
Function<Vip, String> function = new Function<Vip, String>() {
@Override
public String apply(Vip vip) {
return vip.getName();
}
};
Vip vip = new Vip("老张");
System.out.println(function.apply(vip));
// Lambda表达式
//Function<Vip, String> function2 = (Vip v) -> {return v.getName();};
Function<Vip, String> function2 = v -> v.getName();
System.out.println(function2.apply(vip));
// 使用"特殊方法引用"来进行精简
Function<Vip, String> function3 = Vip::getName;
System.out.println(function3.apply(vip));
}
}
class Vip {
private String name;
@Override
public String toString() {
return "Vip{" +
"name='" + name + '\'' +
'}';
}
public Vip(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

(4)、构造方法引用
1.语法格式
-
语法
类名 :: new
-
特点
在Lambda表达式的方法体中返回指定"类名"来创建出来的对象
-
要求
创建对象所调用构造方法形参列表 和 函数式接口中的方法的形参列表 保持一致,并且方法的返回值类型和创建对象的类型保持一致

2.使用示例
【示例】
package com.longdidi.lambda;
import java.util.function.Function;
/*
* Description: 构造方法引用
*/
public class LambdaDemo17 {
public static void main(String[] args) {
// 匿名内部类的方式
// 使用转换型的函数式接口
Function<String, Person> function = new Function<String, Person>() {
@Override
public Person apply(String name) {
return new Person(name);
}
};
System.out.println(function.apply("张三"));
// Lambda表达式的形式
Function<String, Person> function1 = name -> new Person(name);
System.out.println(function1.apply("李四"));
// 使用 构造方法引用 精简
Function<String, Person> function2 = Person::new;
System.out.println(function2.apply("李四"));
}
}
class Person {
private String name;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name) {
this.name = name;
}
}

(5)、数组引用
1.语法格式
-
语法
数组类型 :: new
-
特点
在Lambda表达式的方法体中创建并返回指定类型的"数组"
-
要求
重写的方法有且只有一个整数型的参数,并且该参数就是用于设置数组的空间长度,并且重写方法的返回值类型和创建数组的类型保持一致

2.使用示例
【示例】
package com.longdidi.lambda;
import java.util.Arrays;
import java.util.function.Function;
/*
* Description: 数组引用
* 语法格式:
* 数组::new
* 条件:
* 1. 函数式接口中的方法只有一个整数型参数。
* 2. 这个整数型参数正好是数组的长度。
* 3. 返回值类型相同。
*/
public class LambdaDemo18 {
public static void main(String[] args) {
// 匿名内部类的方式
// 转换型函数式接口
Function<Integer, int[]> function = new Function<Integer, int[]>() {
@Override
public int[] apply(Integer integer) {
return new int[integer];
}
};
int[] nums = function.apply(10);
System.out.println(Arrays.toString(nums));
// Lambda表达式
Function<Integer, int[]> function1 = length -> new int[length];
nums = function1.apply(20);
System.out.println(Arrays.toString(nums));
// 构造方法引用 精简
Function<Integer, int[]> function2 = int[]::new;
nums = function2.apply(30);
System.out.println(Arrays.toString(nums));
}
}

5、Lambda在集合当中的使用
为了能够让Lambda和Java的集合类集更好的一起使用,集合当中也新增了部分方法,以便与Lambda表达式对接
1.5.1、forEach()方法
在Collection集合和Map集合中都提供了forEach()方法用于遍历集合
在Collection集合中提供的forEach()方法的形参为Consumer接口(消费型接口)
通过该方法再配合Lambda表达式就可以遍历List和Set集合中的元素
(1)、遍历List
package com.longdidi.lambda;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
/*
* 使用集合提供的forEach方法,结合Lambda表达式。
* 遍历List集合。
*/
public class LambdaDemo19 {
public static void main(String[] args) {
// List集合
List<Integer> list = Arrays.asList(1, 3, 2, 5, 6, 3, 2, 2, 4, 5, 5, 2);
// 遍历List集合,调用forEach方法
// forEach方法的参数是一个函数式接口:Consumer (消费型接口)
// 匿名内部类方式
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer elt) {
// elt 代表的就是集合中的每个元素
System.out.println(elt);
}
});
System.out.println("========================");
// Lambda表达式改进
list.forEach(elt -> System.out.println(elt));
System.out.println("========================");
// 使用方法引用精简
list.forEach(System.out::println);
}
}

(2)、遍历Set
package com.longdidi.lambda;
import java.util.TreeSet;
/*
* 使用集合提供的forEach方法,结合Lambda表达式。
* 遍历Set集合。
*/
public class LambdaDemo20 {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(100);
treeSet.add(100);
treeSet.add(200);
treeSet.add(300);
treeSet.add(400);
treeSet.add(5);
// 遍历set集合
treeSet.forEach(System.out::println);
}
}

(3)、遍历Map
package com.longdidi.lambda;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/*
* 使用集合提供的forEach方法,结合Lambda表达式。
* 遍历Map集合。
*/
public class LambdaDemo21 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "jack");
map.put(2, "lucy");
map.put(3, "wangwu");
map.put(4, "zhangsan");
map.put(5, "lisi");
// 遍历
// 匿名内部类方式
map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer key, String value) {
System.out.println(key + "=" + value);
}
});
// 使用Lambda表达式改进
map.forEach((k, v) -> System.out.println(k + "=" + v));
}
}

1.5.2、removeIf()方法
在Collection集合中提供的removeIf()方法的形参为Predicate接口(判断型接口)
通过该方法再配合Lambda表达式就可以遍历List和Set集合中的元素
package com.longdidi.lambda;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
/*
* Description:
* 集合的removeIf方法。结合Lambda表达式,删除符合条件的元素。
* 遍历过程中删除符合条件的元素。
* 使用的函数式接口是:判断型。(返回boolean类型的方法。)
*/
public class LambdaDemo22 {
public static void main(String[] args) {
// 创建List集合
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
// 删除 cc
// 匿名内部类的方式
list.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return "cc".equals(s);
}
});
// 使用Lambda表达式方式
list.removeIf(s -> "dd".equals(s));
// 使用方法引用继续精简
list.removeIf("bb"::equals);
System.out.println(list);
// Set集合中的元素在遍历过程中删除符合条件的元素
Set<String> set = new HashSet<>();
set.add("100");
set.add("200");
set.add("300");
set.add("400");
set.removeIf("300"::equals);
System.out.println(set);
}
}

更多推荐

所有评论(0)