Java Lambda 表达式

在本文中,我们将通过示例了解Java lambda表达式,以及lambda表达式与函数接口、泛型函数接口和流API的使用。

lambda表达式是在Java 8中首次引入的。其主要目的是提高语言的表达能力。

但是,在学习lambda之前,我们首先需要了解功能接口。

什么是功能接口?

如果Java接口仅包含一个抽象方法,则将其称为功能接口。仅这一种方法指定了接口的预期用途。

例如,包java.lang中的Runnable接口;是一个功能接口,因为它只组成一个方法,即run()。

示例1:在java中定义功能接口

importjava.lang.FunctionalInterface;@FunctionalInterfacepublicinterfaceMyInterface{//单一抽象方法doublegetValue();}

在上面的示例中,接口MyInterface只有一个抽象方法getValue()。因此,它是一个功能接口。

在这里,我们使用了注解@FunctionalInterface。该注解会强制Java编译器指示该接口是功能接口。因此,不允许有多个抽象方法。但是,它不是强制性的。

在Java 7中,功能接口被视为单一抽象方法(SAM)类型。在Java 7中,SAM类型通常是通过匿名类实现的。

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

publicclassFunctionInterfaceTest{publicstaticvoidmain(String[]args){//匿名类newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("我刚刚实现了Runnable功能接口。");}}).start();}}

输出

我刚刚实现了Runnable功能接口。

在这里,我们可以将匿名类传递给方法。这有助于用Java 7编写代码更少的程序。但是,语法仍然很困难,需要大量的额外代码行。

Java 8进一步扩展了SAM的功能。由于我们知道功能接口只有一个方法,因此在将其作为参数传递时,无需定义该方法的名称。Lambda表达式使我们能够做到这一点。

Lambda表达式简介

Lambda表达式本质上是一个匿名或未命名的方法。lambda表达式不能单独执行。相反,它用于实现功能接口定义的方法。

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

这是我们如何在Java中定义lambda表达式。

(parameterlist)->lambdabody

使用的新运算符(->)被称为箭头运算符或lambda运算符。让我们探索一些实例,

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

doublegetPiValue(){return3.1415;}

我们可以使用lambda表达式编写此方法,如下所示:

()->3.1415

在此,该方法没有任何参数。因此,运算符的左侧包括一个空参数。右侧是lambda主体,用于指定lambda表达式的操作。在这种情况下,它将返回值3.1415。

Lambda 主体的类型

在Java中,lambda主体有两种类型。

1.单个表达式主体

()->System.out.println("Lambdasaregreat");

这种类型的lambda主体称为表达式主体。

2.由代码块组成的主体。

()->{doublepi=3.1415;returnpi;};

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

注意:对于块体,您应该始终有一个return语句。但是,单个表达式主体不需要return语句。

示例3:Lambda表达式

让我们编写一个Java程序,该程序使用lambda表达式返回Pi的值。

如前所述,lambda表达式不是单独执行的。相反,它形成了由功能接口定义的抽象方法的实现。

因此,我们需要首先定义一个功能接口。

importjava.lang.FunctionalInterface;//这是功能接口@FunctionalInterfaceinterfaceMyInterface{//抽象方法doublegetPiValue();}publicclassMain{publicstaticvoidmain(String[]args){//声明对MyInterface的引用MyInterfaceref;//lambda表达式ref=()->3.1415;System.out.println("Pi="+ref.getPiValue());}}

输出

Pi=3.1415

在以上示例中,

  • 我们创建了一个名为MyInterface的功能接口。它包含一个名为getPiValue()的抽象方法

  • 在Main类内部,我们声明了对MyInterface的引用。请注意,我们可以声明接口的引用,但不能实例化接口。那是因为,

    //它将抛出一个错误MyInterfaceref=newmyInterface();//这是有效的MyInterfaceref;
  • 然后,我们为引用分配了一个lambda表达式。

    ref=()->3.1415;
  • 最后,我们使用reference接口调用方法getPiValue()。

    System.out.println("Pi="+ref.getPiValue());

带参数的Lambda表达式

到现在为止,我们已经创建了不带任何参数的lambda表达式。但是,类似于方法,lambda表达式也可以具有参数。例如,

(n)->(n%2)==0

在此,括号内的变量n是传递给lambda表达式的参数。Lambda主体接受参数并检查其是偶数还是奇数。

示例4:将lambda表达式与参数一起使用

@FunctionalInterfaceinterfaceMyInterface{//抽象方法Stringreverse(Stringn);}publicclassMain{publicstaticvoidmain(String[]args){//声明对MyInterface的引用//将lambda表达式分配给引用MyInterfaceref=(str)->{Stringresult="";for(inti=str.length()-1;i>=0;i--){result+=str.charAt(i);}returnresult;};//调用接口的方法System.out.println("Lambdareversed="+ref.reverse("Lambda"));}}

输出

Lambdareversed=adbmaL

泛型功能接口

到目前为止,我们已经使用了仅接受一种类型的值的功能接口。例如,

@FunctionalInterfaceinterfaceMyInterface{StringreverseString(Stringn);}

上面的功能接口仅接受String并返回String。但是,我们可以使功能接口通用,以便接受任何数据类型。如果不熟悉泛型,请访问Java泛型

示例5:泛型功能接口和Lambda表达式

//GenericInterface.java@FunctionalInterfaceinterfaceGenericInterface<T>{//泛型方法Tfunc(Tt);}//GenericLambda.javapublicclassMain{publicstaticvoidmain(String[]args){//声明对GenericInterface的引用//GenericInterface对String数据进行操作//为其分配一个lambda表达式GenericInterface<String>reverse=(str)->{Stringresult="";for(inti=str.length()-1;i>=0;i--)result+=str.charAt(i);returnresult;};System.out.println("Lambdareversed="+reverse.func("Lambda"));//声明对GenericInterface的另一个引用//GenericInterface对整数数据进行操作//为其分配一个lambda表达式GenericInterface<Integer>factorial=(n)->{intresult=1;for(inti=1;i<=n;i++)result=i*result;returnresult;};System.out.println("5的阶乘="+factorial.func(5));}}

输出

Lambdareversed=adbmaL5的阶乘=120

在上面的示例中,我们创建了一个名为GenericInterface的泛型功能接口。它包含一个名为func()的泛型方法。

在类内部:

  • GenericInterface<String> reverse - 创建对该接口的引用。 现在,该接口可以处理String类型的数据。

  • GenericInterface<Integer> factorial -创建对该接口的引用。 在这种情况下,该接口对Integer类型的数据进行操作。

Lambda表达式和流API

新的java.util.stream包已添加到JDK8中,它允许java开发人员执行搜索、过滤、映射、减少等操作,或者操作列表等集合。

例如,我们有一个数据流(在我们的示例中是一个字符串列表),其中每个字符串都是国家名称和国家/地区的组合。 现在,我们可以处理此数据流,并且仅从Nepal检索位置。

为此,我们可以结合使用Stream API和Lambda表达式在流中执行批量操作。

示例6:演示将lambda与Stream API一起使用

importjava.util.ArrayList;importjava.util.List;publicclassStreamMain{//使用ArrayList创建一个列表对象staticList<String>places=newArrayList<>();//准备我们的数据publicstaticListgetPlaces(){//将地点和国家添加到列表中places.add("Nepal,Kathmandu");places.add("Nepal,Pokhara");places.add("India,Delhi");places.add("USA,NewYork");places.add("Africa,Nigeria");returnplaces;}publicstaticvoidmain(String[]args){List<String>myPlaces=getPlaces();System.out.println("PlacesfromNepal:");myPlaces.stream().filter((p)->p.startsWith("Nepal")).map((p)->p.toUpperCase()).sorted().forEach((p)->System.out.println(p));}}

输出

PlacesfromNepal:NEPAL,KATHMANDUNEPAL,POKHARA

在上面的示例中,请注意以下语句:

myPlaces.stream().filter((p)->p.startsWith("Nepal")).map((p)->p.toUpperCase()).sorted().forEach((p)->System.out.println(p));

在这里,我们使用的是Stream API的filter(),map()和forEach()之类的方法。 这些方法可以将lambda表达式作为输入。

我们还可以根据上面学习的语法定义自己的表达式。如上例所示,这使我们可以大大减少代码行。

编辑于2024-05-20 13:04