Lambda 表达式

153 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情

Lambda 表达式介绍

Lambda是一个匿名函数,即没有函数名的函数

Lambda 表达式将函数式编程引入了 Java 语言中,它可以在多种场景下简化程序,并且可以突破传统形式的参数传递:在以前,方法的参数只能是一个变量或普通表达式,但在 JDK 8 以后,Lambda 表达式也可以作为方法的参数来传递,实际上 Lambda 表达式代表着一个匿名方法,因此换句话说,现在可以让一个方法作为另一个方法的参数存在。

刚刚提到Lambda 表达式代表着一个匿名方法,而匿名方法是由参数列表和方法体两部分组成的,Lambda 表达式也必须包含这两部分。即Lambda表达式简化的是方法

Java 中的 Lambda 表达式是用箭头符号(–>)将参数列表和方法体连接起来的,因此一个 Lambda 表达式由以下三部分组成。

  1. 用逗号分隔的参数列表;
  2. 箭头符号(–>);
  3. 方法体(表达式或代码块)。

Lambda 表达式使用

之前我们创建并启动线程是这样的。

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}) .start();

这里new Thread()是一个构造方法,其参数是 Runnable 接口类型的,并且根据多线程的知识可知在 Runnable 接口中只有 run()一个方法。通过观察发现,run()方法的参数列表为空,并且方法体就是{System.out.println("Hello World");},因此 run()方法对应的 Lambda 表达式形式就是()->{System.out.println("Hello World");}

用Lambda就是这样

/**
 * Lambda 表达式简化代码
 */
public class TestLambda {
    public static void main(String[] args) {
        new Thread(
            //Lambda表达式
            () -> {  System.out.println("Hello World");  }
        ) .start();
    }
}

函数式接口和Lambda 表达式配合使用

函数式接口介绍

在TestLambda 中的代码,在用 Lambda 表达式简化 new Thread(Runnable r) 的参数时,这个 Runnable 类型参数有什么特点呢? 🤔

在 Runnable 接口中,有且仅有一个抽象方法!也就是说,在使用 Lambda 代替匿名内部类时,该内部类(或接口)中只能有一个抽象方法。

从 JDK 8 开始,如果一个接口中有且仅有一个抽象方法,那么这个接口就称为函数式接口,并且可以选择性的使用 @FunctionalInterface 注解进行标注。@FunctionalInterface 是 JDK 8 引入的内建注解。

在 JDK 8 以后定义 Runnable 的源码如下所示:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

为了方便使用 Lambda 表达式,JDK8 提供了 java.util.function 包,该包下面的接口全部都是函数式接口。

其中最常用的是 Consumer、Supplier、Function 和 Predicate 四个接口,通常它们称为四大函数式接口

函数式接口接口中定义的唯一抽象方法含义
Consumervoid accept(T t)有输入参数,无返回值,称为消费型接口。
SupplierT get()无输入参数,有返回值,称为供给型接口。
Function<T, R>R apply(T t)有输入参数,有返回值,称为函数型接口。
Predicateboolean test(T t);对输入进行断言,并将断言的结果以布尔形式返回,称为断言型接口。

Lambda 实现自定义函数式接口

第一步:定义一个函数接口

// 自定义的函数式接口 
@FunctionalInterface interface MyLambda { 
void method(); 
}

第二步:使用自定义函数接口

public class TestLambda2 { 
public TestLambda2(MyLambda lambda) { 
lambda.method(); 
} 
public static void main(String[] args) { 
new TestLambda2(
() -> { System.out.println("Hello Lambda"); 
}); 
}

Lambda 表达式除了作为方法的参数以外,还能够作为赋值运算符的右值。

如供给型函数式接口 Supplier 中定义了一个没有输入参数、有返回值的 get() 函数,那么凡是符合以下形式的表达式:

() -> {return ...}

Lambda 表达式使用函数型接口

Function 接口来进行操作:

// Lambda表达式
Function<String, Integer> func = (String arg) -> {
  return Integer.parseInt(arg);
};
// 省略了输入参数的类型
Function<String, Integer> func1 = (arg) -> {
  return Integer.parseInt(arg);
};
// 省略了输入参数类型、return关键字和{}
Function<String, Integer> func2 = (arg) -> Integer.parseInt(arg);

在使用 Lambda 表达式的形式给变量赋值之后,如何使用呢?

赋值符号的左侧 Function 接口中定义了一个抽象方法 Integer apply(String t),赋值符号的右侧是用 Lambda 表达性实现的一个匿名方法,二者就是一对一的对应关系。

换句话说,程序中 Lambda 表达式就是对 Function 接口中 apply() 的具体实现。

也就是在调用赋值符号左侧对象的 apply() 方法时,就会执行赋值符号右侧 Lambda 表达式。

使用 Lambda 表达式对集合进行遍历

public static void main(String[] args) {
ArrayList<String> names = new ArrayList(); 
names.add("张三");
names.add("李四");
names.add("王五");
names.add("赵六");
names.forEach(name -> System.out.println(name)); }

总结

  1. Lambda表达式前提是在 函数式接口里面(函数式接口:一个接口里面有且仅有一个抽象方法),使用Lambda表达式的目标是简化这个唯一的抽象方法
  2. 多态:模拟汉语是“歧义”。 比如打:打什么?打球还是打水是不一样的。 但是Lambda表达式是没有歧义的,因为他用于简化的方法是唯一的,特定的
  3. 怎么知道Lambda简化是哪个方法呢?比如

image.png

在这里我们可以发现,Lambda出现位置是Thread构造方法里面,然后查询Thread构造方法,发现他构造方法是一个Runable接口,然后查询Runable发现他里面只有一个run方法,所以这里的Lambda简化的是run方法