您的位置 首页 java

Java Lambda 表达式——小白快速入门篇

引言

在这篇文章,我们将学习 Lambda 表达式 以及 Lambda 表达式与函数式接口的使用, 泛型 函数式接口,并演示流式传输 API。

Lambda 表达式是在 java 8 中首次引入的,它的主要目的是提高编程语言的表达能力,简化冗余代码。

不过,在进入深入学习匿名函数之前,我们首先需要了解函数式接口。

什么是函数式接口?

如果一个 Java 接口 包含一个且只有一个抽象方法,那么它被称为函数式接口。这样只有有一个方法规定了接口的预期结果类型。

示例 1:在 Java 中定义一个函数式接口

 @FunctionalInterface
public interface DemoInterface {
    // 单一抽象方法
     Integer  getValue();
}
复制代码  

在上面的例子中,接口 DemoInterface 只有一个抽象方法 getValue()。因此,它是一个功能接口。 在这里,我们使用了注解 @FunctionalInterface,此注解的作用是强制 Java 编译器 指示此接口为函数式接口。
在 Java 7 中,函数式接口被认为是Single Abstract Method或 SAM 类型,在 Java 7中,SAM通常用匿名类来实现。

示例 2:在 Java 中使用匿名类实现 SAM

 public class  Function InterfaceTest {
    public  static  void main(String[] args) {
        // 匿名类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我刚刚实现了 Runnable 的函数接口。");
            }
        }).start();
    }
}
复制代码  

输出:

 我刚刚实现了 Runnable 的函数接口。
复制代码  

在这里,我们可以将匿名类传递给方法。这有助于在 Java 7 中使用更少的代码编写程序。但是,语法仍然很困难,并且需要大量冗余的代码行。
Java 8 进一步扩展了 SAM 的功能。由于我们知道函数式接口只有一个方法,因此在将其作为参数传递时不需要定义该方法的名称。 Lambda 表达式恰好可以做到这一点。

Lambda 表达式简介

Lambda 表达式本质上是一个匿名或未命名的方法。Lambda 表达式不会自行执行。相反,它用于实现由函数式接口定义的方法。

如何在 Java 中定义 Lambda 表达式?

以下是我们如何在 Java 中定义 Lambda 表达式。

 (参数集合) -> { Lambda 结构体 }
复制代码  

使用的新运算符 -> 称为箭头运算符或 Lambda 运算符。或许语法可能说不太清楚。让我们体验一些示例:

假设,我们有一个这样的方法:

 String getHubUrl() {
    return "#34;;
}
复制代码  

我们可以使用 Lambda 表达式将此方法编写为:

 () -> "#34;;
复制代码  

这里,该方法没有任何参数。因此,运算符的左边包括一个空参数。右边是λ主体,指定λ表达式的动作。在这种情况下,它返回值 。

Lambda 方法体的类型

在 Java 中,Lambda 主体有两种类型。
1. 单一表达式的主体

 () -> System.out.println("Lambdas test.");
复制代码  

2. 由代码块组成的主体

 () -> {
    String sameSexUrl = "#34;;
    return sameSexUrl;
};
复制代码  

这种类型的 Lambda 主体称为代码块。代码块允许 ambda 体包含多个语句。这些语句包含在大括号内,您必须在大括号后添加一个 分号

示例 3: Lambda 表达式

让我们编写一个使用 Lambda 表达式返回一个网址的 Java 程序。
如前面提到的,一个 Lambda 表达式并不能自行执行。确切地说,它构成了函数式接口所定义的抽象方法的实现。
所以,我们需要先定义一个函数式接口:

 @FunctionalInterface
public interface DemoInterface {
    // 单一抽象方法
    String getUrl();
}
public class DemoLambda {
    public static void main(String[] args) {
        // 声明对 DemoInterface 的引用
        DemoInterface demoInterface;
        // Lambda 表达式
        demoInterface = () -> "#34;;
        System.out.println("同行交流网站: " + demoInterface.getUrl());
    }
}
复制代码  

输出:

 同行交流网站: 
复制代码  
  • 在上述的例子中,我们创建了一个名为 DemoInterface 的函数式接口。它包含一个名为 getUrl() 的抽象方法。
  • 在 DemoLambda 类中,我们声明了对 DemoInterface 的引用。请注意,我们可以声明接口的引用,但不能实例化接口。比如:
 // 会抛出错误
DemoInterface ref = new DemoInterface();
// 正确编译
DemoInterface ref;
复制代码  
  • 然后我们为引用分配了一个单一的 Lambda 表达式。
 demoInterface = () -> "#34;;
复制代码  
  • 最后,我们使用引用接口调用方法 getUrl(),在如下时刻:
 System.out.println("同行交流网站: " + demoInterface.getUrl());
复制代码  

带参数的 Lambda 表达式

到目前为止,我们已经创建了没有任何参数的 Lambda 表达式。但是,与调用方法类似,Lambda 表达式也可以有参数。例如:

 (a,b) -> a + b
复制代码  

如上代码,括号内的变量 a 与 b 是传递给 Lambda 表达式的参数。 Lambda 主体接受参数并对它们进行相加。

示例 4:使用带参数的 Lambda 表达式

 import java.util.List;
@FunctionalInterface
public interface ParamInterface {
    // 抽象方法
    List<String> getStrList(String str);
}
import java.util.Arrays;
public class ParamMain {
    public static void main(String[] args) {
        // 声明对 ParamInterface 的引用
        // 将 lambda 表达式分配给引用
        ParamInterface ref = (str) -> {
            String[] array = str.split("-");
            return Arrays.asList(array);
        };
        // 调用接口的方法
        System.out.println(ref.getStrList("l-m-b-d-a").toString());
    }
}
复制代码  

输出:

 [l, m, b, d, a]
复制代码  

通用函数式接口

到现在为止,我们使用的是只接受一种类型的值的函数式接口。比如说:

 @FunctionalInterface 
public interface DemoInterface { 
    // 单一抽象方法 
    String getUrlByText(String text); 
}
复制代码  

上面的功能接口只接受 String 类型并返回 String 类型。但是,我们可以使这个函数接口通用化,这样就可以接受任何数据类型。如果你对泛型不确定,请先忽略,之后搜索 Java泛型 学习下后再看也不迟。

示例5:泛型函数式接口与 Lambda 表达式

 @FunctionalInterface
public interface GenericsInterface<T> {
    // 泛型方法
    T func(T t);
}
public class GenericsMain {
    public static void main(String[] args) {
        // 声明对 GenericsInterface 的一个引用, 针对字符进行操作
        // 为其分配一个 lambda 表达式
        GenericsInterface<String> ref = (str) ->  str.toUpperCase();
        System.out.println("lambda 转为大写: " + ref.func("lambda"));
        // 声明对 GenericsInterface 的另一个引用, 针对整数进行操作
        // 为其分配一个 lambda 表达式
        GenericsInterface<Integer> ref2 = (n) -> n * 100;
        System.out.println("5 * 100 = " + ref2.func(5));
    }
}
复制代码  

输出:

 lambda 转为大写: LAMBDA
5 * 100 = 500
复制代码  

在上面的例子中,我们创建了一个名为 GenericsInterface 的泛型函数接口。它包含一个名为 func() 的泛型方法。
这里,在 GenericsMain 类里面:

  • GenericsInterface<String> ref – 创建一个对该接口的引用。现在该接口对 字符串 类型的数据进行操作。
  • Generic Interface<Integer> ref2 – 创建一个对该接口的引用。在本例中,该接口对整数类型的数据进行操作。

Lambda 表达式和 Stream API

JDK8 中加入了新的 java.util.stream 包,它允许java开发者对集合进行搜索、过滤、映射、还原等操作。
例如,我们有一个数据流(在我们的例子中是一个字符串列表),每个字符串是省市名和地区名的组合。现在,我们可以处理这个数据流,只检索上海的地方。
为此,我们可以通过 Stream API 和 Lambda表达式 的组合在流中进行批量操作。

示例6: 在 Stream API 中使用 Lambda 操作的演示

 import java.util.ArrayList;
import java.util.List;

public class StreamTest {
    // 使用 ArrayList 创建集合对象
    static List<String> regionList = new ArrayList<String>() {{
        add("上海市,青浦区");
        add("上海市, 闵行区 ");
        add("北京市, 朝阳区 ");
        add("北京市, 海淀区 ");
    }};
    public static void main(String[] args) {
        System.out.println("来自上海的地区:");
        regionList.stream()
                .filter((p) -> p.startsWith("上海"))
                .map((p) -> p.split(",")[1])
                .sorted()
                . forEach ((p) -> System.out.println(p));
    }
}
复制代码  

输出:

 来自上海的地区:
闵行区
青浦区
复制代码  

在上面的例子中,特别注意如下代码:

 regionList.stream()
        .filter((p) -> p.startsWith("上海"))
        .map((p) -> p.split(",")[1])
        .sorted()
        .forEach((p) -> System.out.println(p));
复制代码  

在这里,我们使用的是 Stream API 的 filter()、map() 和 forEach() 等方法。这些方法可以接受一个 Lambda 表达式作为输入。

  • 我们可以根据上面学到的语法定义我们自己的表达式。
  • 这使我们能够大幅减少代码行数,正如我们在上面的例子中看到的那样。

作者:程序员鱼丸
链接:

文章来源:智云一二三科技

文章标题:Java Lambda 表达式——小白快速入门篇

文章地址:https://www.zhihuclub.com/198416.shtml

关于作者: 智云科技

热门文章

网站地图