Java 8 中使用 Lambda 表达式简化代码
文章目录
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。
系统环境:
- Java JDK 版本:1.8
参考地址:
一、Lambda 表达式简介
1、什么是 Lambda 表达式
Lambda
表达式是在 JDK 8 中引入的一个新特性,可用于取代大部分的匿名内部类。使用 Lambda
表达式可以完成用少量的代码实现复杂的功能,极大的简化代码代码量和代码结构。同时,JDK 中也增加了大量的内置函数式接口供我们使用,使得在使用 Lambda 表达式时更加简单、高效。
2、为什么需要 Lambda 表达式
谈起为什么需要 Lambda
表达式,那得从函数式编程开始说起。函数式编程
可以简单说是一种编程规范
,也就是如何编写程序的方法论。它属于结构化编程
的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。函数式编程有很多优点,其中包括:
- 易于并发编程;
- 代码的热升级;
- 更方便的代码管理;
- 代码简洁,开发快速;
- 接近自然语言,易于理解;
函数式编程在 C#
、Python
、JavaScript
中都得到充分体现,在 Java 8 版本中也得到了支持。最明显的就是对 Lambda
表达式的支持。很多种迹象表明未来编程语言将是逐渐融合各自的特性,而不是单纯的声明式语言函数编程语言。将来声明式编程语言借鉴函数编程思想,函数编程语言融合声明式编程特性,这几乎是一种必然趋势。
在 Java 中主要引入 Lambda 表达式的作用是对现有编码语义的优化,减少语法冗余。轻量级的将代码封装为数据,使代码简洁,易于理解。
二、函数式接口和定义
1、什么是函数式接口
函数式接口(Functional Interface)是一个有且仅有一个抽象方法,但是可以有多个非抽象方法
的接口。Java 中函数式接口被隐式转换为 Lambda 表达式,只有保证接口类中有且只有一个抽象方法,Java 中的 Lambda 表达式才能对该方法进行推导。
2、函数式接口格式
在 Java 函数式接口类中,需要满足接口类中只能有一个抽象方法。总结如下:
- 接口有且仅有一个抽象方法;
- 允许定义静态方法;
- 允许定义默认方法;
- 允许 java.lang.Object 中的 public 方法;
在创建函数式接口时,可以在接口类上面加上@FunctionalInterface
注解,这时编译器就可以对接口结构
进行强制检查
是否符合函数式接口规则,如果不符合规则
就显示错误。当然,这个注解只是用于检查,即使不加上该注解,只要符合函数式接口规则
一样也是函数式接口。
下面创建个演示的函数式接口,如下:
1// @FunctionalInterface 注解说明:
2// 使用该注解来定义接口,编译器会强制检查接口是否符合函数式接口规则(有且仅有一个抽象方法),如果不符合则会报错。
3@FunctionalInterface
4public interface MyInterface{
5 /**
6 * 抽象方法(Jdk 8 以后接口类中方法可以省去 public abstract)
7 */
8 public abstract [返回值类型] [方法名称](参数列表);
9
10 /**
11 * 其它方法(Jdk 8 以后允许接口类中添加"默认方法"与"静态方法" )
12 */
13 ...(略)
14}
按照上面函数式接口,定义一个示例的函数式接口类,代码如下:
1@FunctionalInterface
2public interface MyCollection {
3
4 void push(List list);
5
6}
3、函数式接口和 Lambda 表达式的关系
函数式接口和 Lambda 表达式的关系可以总结为:
- 函数式接口只包含一个操作方法;
- Lambda 表达式只能操作一个方法;
- Java 中的 Lambda 表达式核心就是一个函数式编程接口的实现。
4、当前 JDK 8 中存在的函数式接口类
在 JDK 1.8 之前,已经存在部分函数式接口,如下:
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
在 JDK 8 中新增了函数接口 java.util.function 包,里面包含了很多用来支持 Java 的函数式编程的接口类,如下:
类名称 | 描述信息 |
---|---|
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果。 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果。 |
BinaryOperator<T> | 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果。 |
BiPredicate<T,U> | 代表了一个两个参数的 boolean 值方法。 |
BooleanSupplier | 代表了 boolean 值结果的提供方。 |
Consumer<T> | 代表了接受一个输入参数并且无返回的操作。 |
DoubleBinaryOperator | 代表了作用于两个 double 值操作符的操作,并且返回了一个 double 值的结果。 |
DoubleConsumer | 代表一个接受 double 值参数的操作,并且不返回结果。 |
DoubleFunction<R> | 代表接受一个 double 值参数的方法,并且返回结果。 |
DoublePredicate | 代表一个拥有 double 值参数的 boolean 值方法。 |
DoubleSupplier | 代表一个 double 值结构的提供方。 |
DoubleToIntFunction | 接受一个 double 类型输入,返回一个 int 类型结果。 |
DoubleToLongFunction | 接受一个 double 类型输入,返回一个 long 类型结果。 |
DoubleUnaryOperator | 接受一个参数同为类型 double,返回值类型也为 double。 |
Function<T,R> | 接受一个输入参数,返回一个结果。 |
IntBinaryOperator | 接受两个参数同为类型 int,返回值类型也为 int。 |
IntConsumer | 接受一个 int 类型的输入参数,无返回值。 |
IntFunction<R> | 接受一个 int 类型输入参数,返回一个结果。 |
IntPredicate | 接受一个 int 输入参数,返回一个布尔值的结果。 |
IntSupplier | 无参数,返回一个 int 类型结果。 |
IntToDoubleFunction | 接受一个 int 类型输入,返回一个double类型结果。 |
IntToLongFunction | 接受一个 int 类型输入,返回一个 long 类型结果。 |
IntUnaryOperator | 接受一个参数同为类型 int,返回值类型也为 int。 |
LongBinaryOperator | 接受两个参数同为类型 long,返回值类型也为 long。 |
LongConsumer | 接受一个 long 类型的输入参数,无返回值。 |
LongFunction<R> | 接受一个 long 类型输入参数,返回一个结果。 |
LongPredicate | R接受一个 long 输入参数,返回一个布尔值类型结果。 |
LongSupplier | 无参数,返回一个结果 long 类型的值。 |
LongToDoubleFunction | 接受一个 long 类型输入,返回一个 double 类型结果。 |
LongToIntFunction | 接受一个 long 类型输入,返回一个 int 类型结果。 |
LongUnaryOperator | 接受一个参数同为类型 long,返回值类型也为 long。 |
ObjDoubleConsumer<T> | 接受一个 object 类型和一个 double 类型的输入参数,无返回值。 |
ObjIntConsumer<T> | 接受一个 object 类型和一个 int 类型的输入参数,无返回值。 |
ObjLongConsumer<T> | 接受一个 object 类型和一个 long 类型的输入参数,无返回值。 |
Predicate<T> | 接受一个输入参数,返回一个布尔值结果。 |
Supplier<T> | 无参数,返回一个结果。 |
ToDoubleBiFunction<T,U> | 接受两个输入参数,返回一个 double 类型结果 |
ToDoubleFunction<T> | 接受一个输入参数,返回一个 double 类型结果。 |
ToIntBiFunction<T,U> | 接受两个输入参数,返回一个 int 类型结果。 |
ToIntFunction<T> | 接受一个输入参数,返回一个 int 类型结果。 |
ToLongBiFunction<T,U> | 接受两个输入参数,返回一个 long 类型结果。 |
ToLongFunction<T> | 接受一个输入参数,返回一个 long 类型结果。 |
UnaryOperator<T> | 接受一个参数为类型 T,返回值类型也为 T。 |
5、JDK 中常见的函数式接口类
上面 java.util.function 包提供了众多的函数式接口,其中常用的有:
- java.util.function.Predicate<T>:接收参数对象 T,返回一个 boolean 类型结果。
- java.util.function.Comsumer<T>:接收参数对象 T,不返回结果。
- java.util.function.Function<T,R>:接收参数对象 T,返回结果对象 R。
- java.util.function.Supplier<T>:不接收参数,提供 T 对象的创建工厂。
- java.util.function.UnaryOperator<T>:接收参数对象 T,返回结果对象 T。
- java.util.function.BinaryOperator<T>:接收两个 T 对象,返回一个 T 对象结果。
注:为了使易懂,下面示例中 Lambda 表达式没有使用的最简易写法,而是使用比较繁琐的写法。
(1)、java.util.function.Predicate<T>
- 接口类作用: 接收参数对象T,返回一个 boolean 类型结果。
- 接口类源码:
1@FunctionalInterface
2public interface Predicate<T> {
3 /** abstract 方法,接收一个参数, 判断这个参数是否匹配某种规则, 然后返回布尔值结果 */
4 boolean test(T t);
5
6 /** default 方法,接收另外一个 Predicate<T> 类型参数进行"逻辑与"操作,返回一个新的 Predicate */
7 default Predicate<T> and(Predicate<? super T> other) {
8 Objects.requireNonNull(other);
9 return (t) -> test(t) && other.test(t);
10 }
11
12 /** default 方法,接收另外一个 Predicate<T> 类型参数进行"逻辑或"操作,返回一个新的 Predicate */
13 default Predicate<T> negate() {
14 return (t) -> !test(t);
15 }
16
17 /** default 方法,返回当前 Predicate 取反操作之后的 Predicate */
18 default Predicate<T> or(Predicate<? super T> other) {
19 Objects.requireNonNull(other);
20 return (t) -> test(t) || other.test(t);
21 }
22
23 static <T> Predicate<T> isEqual(Object targetRef) {
24 return (null == targetRef)
25 ? Objects::isNull
26 : object -> targetRef.equals(object);
27 }
28}
- 使用示例:
1public class PredicateExample {
2
3 /** 这里创建一个 Prodicate,设置验证秘钥的一个逻辑,然后返回并输出验证结果 */
4 public static void main(String[] args) {
5 // 创建 Predicate 及 Lambda 表达式与待实现的逻辑
6 Predicate<String> validation = (String secret) -> {
7 return "123456".equals(secret);
8 };
9 // 调用 Predicate 提供的 test 方法并输出结果
10 System.out.println(validation.test("123"));
11 System.out.println(validation.test("123456"));
12 }
13
14}
日常开发中,需要对某个值进行判断操作,并且返回判断结果,这时可以考虑使用 Predicate 接口,以及它的 Lambda 表达式的实现,能方便的实现我们业务上的一些功能。
(2)、java.util.function.Comsumer<T>
- 接口类作用: 接收参数对象 T,不返回结果。
- 接口类源码:
1@FunctionalInterface
2public interface Consumer<T> {
3
4 /** abstract 方法,接收一个参数,执行消费逻辑 */
5 void accept(T t);
6
7 /** default 方法,将两个 Consumer 连接到一起,再进行消费 */
8 default Consumer<T> andThen(Consumer<? super T> after) {
9 Objects.requireNonNull(after);
10 return (T t) -> { accept(t); after.accept(t); };
11 }
12
13}
- 使用示例:
1/** 这里创建一个 Consumer,模拟发送消息并打印内容 */
2public class ConsumerExample {
3
4 public static void main(String[] args) {
5 // 创建 Consumer 及 Lambda 表达式与待实现的逻辑
6 Consumer<String> consumer = (String message) -> {
7 System.out.println("发送消息内容:" + message);
8 };
9 // 调用 Consumer 提供的 accept 方法
10 consumer.accept("测试消息");
11 }
12
13}
日常开发中,需要对某个类型进行公共处理,并且不需要任何返回值,这时可以考虑使用 Consumer 接口及它的 Lambda 表达式的实现,能方便的实现我们业务上的一些功能。
(3)、java.util.function.Function<T,R>
- 接口类作用: 接收参数对象 T,返回结果对象 R。
- 接口类源码:
1@FunctionalInterface
2public interface Function<T, R> {
3 /**abstract 方法,接收一个参数进行处理,然后返回处理结果 R */
4 R apply(T t);
5
6 /** default 方法,先执行参数(Function)的,再执行调用者(Function) */
7 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
8 Objects.requireNonNull(before);
9 return (V v) -> apply(before.apply(v));
10 }
11
12 /** default 方法,先执行调用者,再执行参数,和compose相反 */
13 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
14 Objects.requireNonNull(after);
15 return (T t) -> after.apply(apply(t));
16 }
17
18 /** 返回当前正在执行的方法 */
19 static <T> Function<T, T> identity() {
20 return t -> t;
21 }
22}
- 使用示例:
1/** 这里创建一个 Function,对传入的参数进行验证,如果包含 a 字符就返回1,否则返回0 */
2public class FunctionExample {
3
4 public static void main(String[] args) {
5 // 创建 Function 及 Lambda 表达式与待实现的逻辑
6 Function<String, Integer> function = (String str) -> {
7 return str.contains("a") ? 1 : 0;
8 };
9 // 调用 Function 提供的 apply 方法
10 System.out.println(function.apply("abcd"));
11 System.out.println(function.apply("efgh"));
12 }
13
14}
日常开发中,需要对某个类型数据进行操作,经过一系列逻辑后转换为一个新的类型进行返回,这时可以考虑使用 Function 接口及它的 Lambda 表达式的实现,能方便的实现我们业务上的一些功能。
(4)、java.util.function.Supplier<T>
- 接口类作用: 不接收参数,提供 T 对象的创建工厂。
- 接口类源码:
1@FunctionalInterface
2public interface Supplier<T> {
3 /** abstract 方法,设置业务逻辑,获取逻辑中创建的对象 */
4 T get();
5}
- 使用示例:
1/** 这里创建一个 Supplier,用于生成随机ID,通过 get 方法获取生成的随机ID值 */
2public class SupplierExample {
3
4 public static void main(String[] args) {
5 // 创建 Supplier 及 Lambda 表达式与待实现的逻辑
6 Supplier<String> supplier = () -> {
7 return UUID.randomUUID().toString();
8 };
9 // 调用 Supplier 提供的 get 方法
10 System.out.println(supplier.get());
11 }
12
13}
日常开发中,需要创建一个统一的工厂用于生成特定的产物完成特定的目标,这时就可以考虑使用 Supplier 接口及它的 Lambda 表达式的实现,能方便的实现我们业务上的一些功能。
(5)、java.util.function.UnaryOperator<T>
- 接口类作用: 接收参数对象 T,返回结果对象 T。
- 接口类源码:
1// 可以看到 UnaryOperator 继承了 Function 接口
2@FunctionalInterface
3public interface UnaryOperator<T> extends Function<T, T> {
4 /** static 方法,接收一个参数,然后对其处理后再返回 */
5 static <T> UnaryOperator<T> identity() {
6 return t -> t;
7 }
8}
- 使用示例:
1/** 这里创建一个 UnaryOperator,接收一个字符串进行加工处理后返回新字符串 */
2public class UnaryOperatorExample {
3
4 public static void main(String[] args) {
5 // 创建 UnaryOperator 及 Lambda 表达式与待实现的逻辑
6 UnaryOperator<String> unaryOperator = (String str) -> {
7 return "[" + str + "]";
8 };
9 // 调用 UnaryOperator 继承的 Function 提供的 apply 方法
10 System.out.println(unaryOperator.apply("hello"));
11 }
12
13}
日常开发中,我们经常要对一个已有的对象进行操作修改,然后返回修改后的对象,这时就可以考虑使用 UnaryOperator 接口及它的 Lambda 表达式的实现,能方便的实现我们业务上的一些功能。
(6)、java.util.function.BinaryOperator<T>
- 接口类作用: 接收两个 T 对象,返回一个 T 对象结果。
- 接口类源码:
1@FunctionalInterface
2public interface BinaryOperator<T> extends BiFunction<T,T,T> {
3 /** abstract 方法,通过比较器Comparator来比较两个元素中较小的一个作为返回值返回 */
4 public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
5 Objects.requireNonNull(comparator);
6 return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
7 }
8
9 /** 通过比较器Comparator来比较两个元素中较大的一个作为返回值返回 */
10 public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
11 Objects.requireNonNull(comparator);
12 return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
13 }
14}
- 使用示例:
1/** 这里创建一个 BinaryOperator,比较传入的两个参数哪个值最大,返回最大值 */
2public class BinaryOperatorExample {
3
4 public static void main(String[] args) {
5 // 创建 BinaryOperator 及 Lambda 表达式与待实现的逻辑
6 BinaryOperator<Integer> binaryOperator = (Integer t1, Integer t2) -> {
7 return t1 > t2 ? t1 : t2;
8 };
9 // 调用 BinaryOperator 继承的 BiFunction 提供的 apply 方法
10 System.out.println(binaryOperator.apply(1, 2));
11 }
12
13}
在使用这几种基本函数接口时传入参数 T 不能是基本类型,如 BinaryOperator
中 T 不能设置为 int,只能使用 Integer 包装类,这也限制了 Lambda 表达式中设置参数时候,使用包装类替换基本类型。
日常开发中,我们有时候会对两个对象进行操作,执行一些操作逻辑后返回结果,这时就可以考虑使用 BinaryOperator 接口及它的 Lambda 表达式的实现,能方便的实现我们业务上的一些功能。
三、Lambda 表达式基本语法
1、Lambda 表达式的组成
Lambda 表达式的组成可以拆分为:
- 声明: 与 Lambda 表达式绑定的接口类型。
- 参数: 参数包含在一对 () 中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
- 操作符: ->
- 执行代码块: 执行代码块包含在一对 {} 中,出现在操作符的右侧。
[接口声明] = (参数) -> {执行代码块}
2、Lambda 表达式的格式
Lambda 表达式可以分为下面几种格式:
- 无参数,无返回值;
- 有一个参数,无返回值;
- 左侧只有一个参数,小括号可以省略不写;
- 有两个以上参数,有返回值,并且Lambda 体中有多条语句;
- 若右侧Lambda体中,只有一条语句,return 和大括号都可以省略不写;
- Lambda 表达式的参数列表的数据类型可以省略不写,jvm编译器会进行上下文推断出,数据类型“类型推断”;
(1)、无参数,无返回值
1() -> System.out.println("测试");
(2)、有一个参数,无返回值
1(x) -> System.out.println(x);
(3)、左侧只有一个参数,小括号可以省略不写
1x -> System.out.println(x);
(4)、有两个以上参数,有返回值,并且Lambda 体中有多条语句
1Comparator<Integer> comparator = (x, y) -> {
2 System.out.println("测试");
3 return Integer.compare(x,y);
4};
(5)、若右侧Lambda体中,只有一条语句,return 和大括号都可以省略不写
1Comparator<Integer> Comparator = (x, y) -> Integer.compare(x, y);
(6)、Lambda 表达式的参数列表的数据类型可以省略不写,JVM 在运行时,会自动根据绑定的抽象方法中的参数,进行数据类型推导
1(Integer x, Integer y) -> Integer.compare();
四、Lambda 表达式中变量作用域
Java 中的变量捕获与变量隐藏:
- 变量捕获: 局部类和匿名内部类可以访问被 final 修饰的封闭块内的局部变量。
- 变量隐藏: 在一个类中,子类中的成员变量如果和父类中的成员变量同名,那么即使他们类型不一样,只要名字一样,父类中的成员变量都会被隐藏。
在局部类和匿名内部类都存在 变量捕获
与 变量隐藏
,而在 Lambda
表达式中则只支持 变量捕获
。
下面是对这作用域得演示示例:
(1)、匿名内部类:
1public class VariableExample {
2 /** 成员变量 */
3 String str1 = "成员变量";
4
5 public void innerClass() {
6 // 方法内部变量
7 String str2 = "方法内部变量";
8 // 使用匿名内部类创建线程
9 new Thread(new Runnable() {
10 // 匿名内部类内部变量
11 String str3 = "匿名内部类内部变量";
12 @Override
13 public void run() {
14 /* 访问变量 */
15 System.out.println("匿名内部类输出:" + str1);
16 System.out.println("匿名内部类输出:" + str2);
17 System.out.println("匿名内部类输出:" + str3);
18 /* 修改变量 */
19 str1 = "修改访问成员变量";
20 // str2 = "修改访问方法内部变量"; // 不能进行修改,默认推导变量的修饰符 final
21 str3 = "修改访问匿名内部类内部变量";
22 /* 在匿名内部类中定义和类外部变量一样名称的变量 */
23 String str1 = "重新命名成员变量";
24 String str2 = "重新命名方法内部变量";
25 }
26 }).start();
27 }
28
29 /** Main 方法 */
30 public static void main(String[] args) {
31 VariableExample variableExample = new VariableExample();
32 // 匿名内部类
33 variableExample.innerClass();
34 }
35
36}
(2)、Lambda 表达式:
1public class VariableExample {
2 /** 成员变量 */
3 String str1 = "成员变量";
4
5 public void lambdaExpression() {
6 // 方法内部变量
7 String str2 = "方法内部变量";
8 new Thread(()->{
9 // Lambda 内部变量
10 String str3 = "Lambda 内部变量";
11 /* 访问变量 */
12 // 访问成员变量
13 System.out.println("Lambda 表达式输出:" + str1);
14 // 访问方法内部变量
15 System.out.println("Lambda 表达式输出:" + str2);
16 // 访问匿名内部类内部变量
17 System.out.println("Lambda 表达式输出:" + str3);
18 /* 修改变量 */
19 str1 = "修改访问成员变量";
20 // str2 = "修改访问方法内部变量"; // 不能进行修改,默认推导变量的修饰符 final
21 str3 = "修改访问匿名内部类内部变量";
22 /* 在 Lambda 中定义和类外部变量一样名称的变量 */
23 String str1 = "重新命名成员变量";
24 // String str2 = "重新命名方法内部变量"; // 不能命名,lambda 不支持变量隐藏
25 }).start();
26 }
27
28 /** Main 方法 */
29 public static void main(String[] args) {
30 VariableExample variableExample = new VariableExample();
31 // 匿名内部类
32 variableExample.innerClass();
33 // Lambda 表达式
34 variableExample.lambdaExpression();
35 }
36
37}
五、Lambda 表达式方法重载问题
当使用 Lambda 表达式,调用一个类中的重载方法,且方法中的参数为都为函数接口,函数接口中定义的方法接收的参数类型相同,这时候 Lambda 是无法推断出要调用哪个方法。
函数接口A:
1@FunctionalInterface
2interface MyInterfaceA {
3 void push(String param);
4}
函数接口B:
1@FunctionalInterface
2interface MyInterfaceB {
3 void pull(String param);
4}
示例,实现方法重载与测试的 Main 方法:
1public class LambdaOverloadExample {
2
3 // 重载方法A
4 public static void method(MyInterfaceA myInterfaceA) {
5 myInterfaceA.push("hello 1");
6 }
7 // 重载方法B
8 public static void method(MyInterfaceB myInterfaceB) {
9 myInterfaceB.pull("Hello 2");
10 }
11
12 /** Main 方法*/
13 public static void main(String[] args) {
14 // 使用匿名内部类
15 method(new MyInterfaceA() {
16 @Override
17 public void push(String param) {
18 System.out.println(param);
19 }
20 });
21 method(new MyInterfaceB() {
22 @Override
23 public void pull(String param) {
24 System.out.println(param);
25 }
26 });
27 // 使用 Lambda 表达式
28 //method(param -> System.out.println(param)); // 编译器提示错误,表示无法推断使用哪个参数
29 }
30
31}
上面注掉的那部分代码,在编辑器中直接提示错误
,很显然 Lambda
表达式无法直接推断
出使用哪个类中的重载方法
。其实,只要明确告诉 Lambda
表达式使用哪个参数
,就可以很简单的解决问题,比如以上面的例子,在 Lambda
表达式使用 method
方法时,将参数类型
转换为对应的要使用的类型
就可以解决这个问题,代码如下:
1// 转换参数为 MyInterfaceA
2method((MyInterfaceA)param -> System.out.println(param));
3// 转换参数为 MyInterfaceB
4method((MyInterfaceB)param -> System.out.println(param));
按上面进行修改后就可以正常使用 Lambda 表达式了,如果不习惯也可以使用匿名内部类进行方法调用,内名内部类是没有相关问题的。
六、Lambda 表达式方法引用
方法引用本质上就是对方法调用的简化,方法引用和函数式接口绑定,在使用过程中会创建函数式接口的实例,是结合 Lambda 表达式的一种特性。在应用过程中,方法引用常分为:
- 静态方法引用
- 实例方法引用
- 构造方法引用
- 特定类型的任意对象实例方法引用
注意:在使用 Lmabda 方法引用时虽然能够简化代码,但是在实际开发中不可因需要简化代码而过度使用方法引用,因为他会在很大程度上降低代码可读性。
1、创建示例的实体类
为了下面示例方便,我们首先创建一个 Person 实体类,如下:
1public class Person {
2 /** 姓名 */
3 private String name;
4 /** 岁数 */
5 private int age;
6
7 public Person() {
8 }
9 public Person(String name, int age) {
10 this.name = name;
11 this.age = age;
12 }
13
14 public String getName() {
15 return name;
16 }
17 public void setName(String name) {
18 this.name = name;
19 }
20 public int getAge() {
21 return age;
22 }
23 public void setAge(int age) {
24 this.age = age;
25 }
26
27 @Override
28 public String toString() {
29 return "Person{name=" + name + ",age=" + age + "}";
30 }
31}
2、静态方法引用示例
静态方法的引用的使用: 静态方法所在类
.方法名称()
--> 静态方法所在类
::
方法名称
创建一个使用静态方法引用的示例类:
1public class StaticMethodExample {
2
3 /** 测试的静态方法 */
4 public static int compareAge(Person p1, Person p2) {
5 return p1.getAge() - p2.getAge();
6 }
7
8 public static void main(String[] args) {
9 // 创建 Person 集合
10 List<Person> personList = new ArrayList<>();
11 personList.add(new Person("Wangqin",26));
12 personList.add(new Person("Liming",22));
13 personList.add(new Person("Alisi",18));
14 personList.add(new Person("Jerry",31));
15
16 // 按岁数进行排序,使用静态方法引用
17 personList.sort(StaticMethodExample::compareAge);
18 }
19
20}
3、实例方法引用示例
实例方法的引用的使用:创建类型对应一个对象
--> 对应应用
::
实例方法名称
创建一个封装实例方法的类:
1public class PersonUtil{
2 /** 测试的实例方法 */
3 public int compareAge(Person p1, Person p2) {
4 return p1.getAge() - p2.getAge();
5 }
6}
创建一个使用实例方法引用的示例类:
1public class InstanceMethodExample {
2
3 public static void main(String[] args) {
4 // 创建 Person 集合
5 List<Person> personList = new ArrayList<>();
6 personList.add(new Person("Wangqin",26));
7 personList.add(new Person("Liming",22));
8 personList.add(new Person("Alisi",18));
9 personList.add(new Person("Jerry",31));
10
11 // 按岁数进行排序,
12 PersonUtil personUtil = new PersonUtil();
13 // 引用实例方法
14 personList.sort(personUtil::compareAge);
15 }
16
17}
4、构造方法引用示例
构造方法的引用的使用:绑定函数式接口
创建一个函数式接口,且设置接收参数和 Person 的构造方法相同,返回 Person 对象的方法定义:
1@FunctionalInterface
2public interface PersonConstructor{
3 Person initInstance(String name, int age);
4}
创建一个使用构造方法引用的示例类
1public class LambdaConstructorMethodExample {
2
3 public static void main(String[] args) {
4 // 创建 Person 集合
5 List<Person> personList = new ArrayList<>();
6 personList.add(new Person("Wangqin", 26));
7 personList.add(new Person("Liming", 22));
8 personList.add(new Person("Alisi", 18));
9 personList.add(new Person("Jerry", 31));
10
11 // 构造方法引用
12 PersonConstructor personConstructor = Person::new;
13 Person person = personConstructor.initInstance("linda", 18);
14 System.out.println(person);
15 }
16
17}
5、特定类型的任意对象实例方法引用示例
特定类型的任意对象实例方法引用示例:特定类型
::
特定类型的方法
以下是对特定类型的任意对象的实例方法的引用的示例:
1public class LambdaExample {
2
3 public static void main(String[] args) {
4 // 创建字符串集合
5 List<String> strList = new ArrayList<>();
6 strList.add("Jerry");
7 strList.add("Mini");
8 strList.add("Kary");
9 strList.add("walls");
10
11 // 使用集合的sort方法,按照String的compareToIgnoreCase进行排序(比较字符串hash值)
12 strList.sort(String::compareToIgnoreCase);
13 System.out.println(strList);
14 }
15
16}
这里根据定义的集合 strList
去推导目标类型参数值,如果不符合
后面传入的方法引用所对应的类型,将报错。该方法参考等效 Lambda
表达式 String::compareToIgnoreCase
的参数列表 (String a, String b)
,其中 a 和 b 是用于更好地描述这个例子中的任意名称。方法引用将调用该方法 a.compareToIgnoreCase(b)
。
---END---
!版权声明:本博客内容均为原创,每篇博文作为知识积累,写博不易,转载请注明出处。