博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 8 新特性(1) - Lambda表达式
阅读量:7251 次
发布时间:2019-06-29

本文共 4099 字,大约阅读时间需要 13 分钟。

hot3.png

Lambda 表达式是 Java 8 最重要的新特性之一,它让我们可以用很简洁的代码完成一个功能。

 

01

举个栗子

 

 

我们知道,Java 中编写多线程主要有两种方式 :继承 Thread 类或实现 Runnable 接口。通常情况下如果逻辑比较简单,我们会写出如下代码:

Thread t1 = new Thread(new Runnable() {    @Override    public void run() {        for (int i = 0; i < 10; i++)            System.out.println("Without Lambda Expression");    }});

上述代码创建一个匿名类传入 Thread 构造函数里。下面我们看 Java 8 中我们怎么使用 Lambda 表达式简化上面的代码。

Thread t2 = new Thread(() -> {    for (int i = 0; i < 10; i++)        System.out.println("With Lambda Expression");});

 

02

函数式接口

 

在介绍 Lambda 之前,我们需要了解 Java 8 中的一个新的概念:函数式接口。

函数式接口的定义:只包含一个方法的抽象接口。可以使用 @FunctionalInterface 注解,将一个接口标注为函数式接口,该接口只能包含一个抽象方法,如果添加第二个抽象方法的话,会编译失败。

 

像比较常用的 java.lang.Runnable、java.util.concurrent.Callable、java.util.Comparator 等都是只包含一个抽象方法的接口,默认都是函数式接口。

7663e34c1bf75bef43c99b499d1c2f95e1b.jpg

dd6447297dd886df4a82f34872f34b93391.jpg

 

函数式接口有如下要求:

  1. 只包含一个抽象方法,如果有继承父接口,则要计算父接口的抽象方法数量。

    7434cc3a3c9585ce060316e850e30a6e32f.jpg

  2. 可以声明从 java.lang.Object 继承来的方法,如果除去继承自 Object 的抽象方法,如果只有一个抽象方法,则该接口也是函数式接口。

    a4148c42935f96e6ac17365c57a4891435b.jpg

  3. 可以有任意数量的默认方法或静态方法。

    7434cc3a3c9585ce060316e850e30a6e32f.jpg

  4. @FunctionalInterface 注解可有可无,只要该接口只有一个抽象方法,则该接口默认为函数式接口,这个注解只是检查它是否是一个函数式接口。

 

Java 8 内置的四大核心函数式接口:

函数式接口 方法 用途
Consumer
void accept(T t)
消费性接口,对类型 T 的对象应用操作
Supplier
T get()
供给型接口,返回类型为 T 的对象
Function
R apply(T t)
应用型接口,对类型为 T 的对象应用操作,并返回类型为 R 的对象
Predicate
boolean test(T t)
断定型接口,判断类型为 T 的对象是否满足某约束

 

03

语法

 

Lambda 表达式的基本语法如下:

 (parameters) -> { statements; }

 

parameters: 参数,可以是一个或多个参数,不必指定参数类型,类型会自动推断,当只有一个参数是,小括号可省略

statements:语句,可以是一行或多行代码,当只有一行代码,大括号可省略,return 也可省略。

 

下面是我们加法和平方运算的接口:

public interface MathAdd {    int add(int x, int y);}public interface MathSquare {    int square(int x);}

我们可以使用下面的方法来使用这两个方法:

MathAdd sum = (int x, int y) -> {
return x + y;}; // 语句1int z = sum.add(1, 2);// 一个参数可省略参数列表的小括号,只有一行语句的话可以省略大括号// 和 return 关键字 MathSquare mul = x -> x*x; // 语句2int y = mul.square(5);

 

 

我们再看上面的 语句1语句2 ,为什么能够等号右边的语句能够赋值给左边的变量呢?这个问题的本质是:一个 Lambda 表达式的类型是什么?

 

答案是:它的类型是由其上下文推导而来。

 

看下面的例子,有两个接口,方法签名完全一样:

public interface MathAdd {    int add(int x, int y);}public interface MathSub {    int sub(int x, int y);}// 测试方法@Testpublic void test2 () {    MathAdd sum = (int x, int y) -> {
return x + y;};    MathSub sub = (int x, int y) -> {
return x + y;};    System.out.println("MathAdd.add(2, 4): "+sum.add(2, 4));    System.out.println("MathSub.sub(2, 4): "+sub.sub(2, 4));}

上面的代码能顺利编译并执行通过,结果如下:

892c49e3e6a64989dce48b868437c443754.jpg

那么同样的语句  (int x, int y) -> {return x + y;} ,为什么既能是 MathAdd 类型又能是 MathSub 类型呢?奥妙就在于 MathAdd、 MathSub 这两个接口的方法签名一样。

 

下面是 Lambda 表达式的目标类型 T 的条件:

  1. T 是一个函数式接口,只有函数式接口才可以使用 Lambda 表达式。

  2. Lambda 表达式的参数和 T 的方法参数在数量和类型上一一对应,注意:类型必须完全一致,如 Integer 和 int 并不能对应上。

  3. Lambda 表达式的返回值和 T 的方法返回值兼容,这里并不要求返回值类型完全一致,只要类型兼容即可,如 Lambda 表达式返回 Integer 和T 的方法返回 int 也是满足的。

  4. Lambda 表达式内所抛出的异常和 T 的方法 throws 类型相兼容,这和条件3的类似的。

 

 

04

方法引用

 

方法引用是lambda表达式的一种简写形式。 如果 lambda 表达式只是调用一个特定的已经存在的方法,则可以使用方法引用,实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致。

 

语法:T::method

 

使用“::”操作符将方法名和对象或类的名字分隔开来。以下是四种使用情况:

  • 实例对象的成员方法,对象::实例方法

  • 静态方法引用,类::静态方法

  • 实例方法引用,类::实例方法

  • 构造器引用,类::new

 

下面的代码是打印字符串,其中 语句1 可改为 语句2 的形式。

Consumer
con = (x) -> System.out.println(x);// 语句1con.accept("test");
//方法引用,对象::实例方法名Consumer
consumer = System.out::println;// 语句2consumer.accept("test");

 

方法引用相比 Lambda 更加简洁,能够用更少的代码量实现相同的功能。

 

下面通过一个我们开发中最常见的排序例子说明:

// 原始集合List
list = Arrays.asList("Lakers", "Celtics", "Bulls", "Nets", "Magic", "Warriors", "Spurs", "Rockets", "Thunder");//  Before java 8Collections.sort(list, new Comparator
() {    public int compare(String x, String y) {        return x.compareTo(y);    }});System.out.println("Before java 8 : "+list);// Lambda expressionsCollections.sort(list, (x, y) -> y.compareTo(x));System.out.println("Lambda 表达式 : "+list);// method referenceCollections.sort(list, String::compareTo);System.out.println("方法引用 : "+list);

输出结果如下:

f31af3f9c618c5714ae280de361befc1e55.jpg

使用 Lambda 能极大的减少代码量,不仅仅是不需要使用匿名类了,而且由于它的类型推断,也能减少很多 import 语句,但是可读性比较差。

 

总结一下,Lambda 的优缺点:

优点:

  1. 简洁,这可能是最大的优点了,Lambda 能减少非常多的代码量;

  2. 非常方便写并行计算,这一点会在下一篇 Stream API 中讲到;

  3. 外部变量不再必须声明为 final 的了;

  4. Lambda 不会引入新的作用域,比如在 Lambda 中使用 this 和在外部使用 this 是同一个对象。

缺点:

  1. 代码可读性变差,这也是优点1所带来的副作用,更少的代码对机器友好但对开发者不友好;特别是类型推断,如果对 API 不熟悉的话读别人的代码会很艰难;

  2. 调试不友好;

  3. 用途有限,只能用于函数式接口。

     

 

识别二维码关注我

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

转载于:https://my.oschina.net/714593351/blog/1930111

你可能感兴趣的文章
C# 里EF 对Mysql DB更新,乱码
查看>>
iOS - IM 即时通讯
查看>>
function的name属性
查看>>
【转载】Deep Belief Networks资料汇总
查看>>
三角螺旋阵 (代码)
查看>>
10.包和访问权限
查看>>
数字信号处理C语言(2) ------带高斯噪声的sin函数和组合sin函数
查看>>
css 梯形标签页
查看>>
理解数据点,自变量和因变量(参数和值)ChartControl
查看>>
机器学习数学基础总结
查看>>
[HP-UX]清空FIN_WAIT_2的连接
查看>>
大白话Vue源码系列(02):编译器初探
查看>>
[Sdoi2016]平凡的骰子
查看>>
mysql定义游标
查看>>
两个有序数组合并算法
查看>>
面向对象设计原则之五:迪米特法则
查看>>
GitHub for Windows简单使用
查看>>
c#操作XML
查看>>
作为一个测试leader平时应该注意哪些方面
查看>>
【DOM编程艺术】Ajax(Hijax)
查看>>