lambda表达式是在Java 8中首次引入的。其主要目的是提高语言的表达能力。
但是,在进入lambda之前,我们首先需要了解功能接口。
什么是功能接口?
如果Java接口仅包含一个抽象方法,则将其称为功能接口。仅这一种方法指定了接口的预期用途。
例如,来自包java.lang
的Runnable
接口;是一个功能接口,因为它仅构成一个方法,即run()
。
示例1:在java中定义功能接口
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
// the single abstract method
double getValue();
}
在上面的示例中,接口MyInterface只有一个抽象方法getValue()。因此,它是一个功能接口。
在这里,我们使用了@FunctionalInterface
注释。该注释会强制Java编译器指示该接口是功能接口。因此,不允许有多个抽象方法。但是,它不是强制性的。
在Java 7中,功能接口被视为单一抽象方法或SAM类型。 SAM通常用Java 7中的匿名类实现。
示例2:使用Java中的匿名类实现SAM
public class FunctionInterfaceTest {
public static void main(String[] args) {
// anonymous class
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("I just implemented the Runnable Functional Interface.");
}
}).start();
}
}
输出 :
I just implemented the Runnable Functional Interface.
在这里,我们可以将匿名类传递给方法。这有助于用Java 7用更少的代码编写程序。但是,语法仍然很困难,并且需要很多额外的代码行。
Java 8进一步扩展了SAM的功能。由于我们知道功能接口只有一个方法,因此在将其作为参数传递时,无需定义该方法的名称。 Lambda表达式使我们能够做到这一点。
Lambda表达式简介
Lambda表达式本质上是一个匿名或未命名的方法。 lambda表达式不能单独执行。相反,它用于实现功能接口定义的方法。
如何在Java中定义Lambda表达式?
这是我们如何在Java中定义lambda表达式。
(parameter list) -> lambda body
使用的新运算符 ( ->
)被称为箭头运算符或lambda 运算符。目前语法尚不清楚。让我们探索一些例子,
假设我们有一个这样的方法:
double getPiValue() {
return 3.1415;
}
我们可以使用lambda表达式编写此方法,如下所示:
() -> 3.1415
在此,该方法没有任何参数。因此, 运算符的左侧包括一个空参数。右侧是lambda主体,用于指定lambda表达式的操作。在这种情况下,它将返回值3.1415。
Lambda身体的类型
在Java中,lambda主体有两种类型。
1.单身的身体
() -> System.out.println("Lambdas are great");
这种类型的lambda主体称为表达式主体。
2.由代码块组成的主体。
() -> {
double pi = 3.1415;
return pi;
};
这种类型的lambda体称为块体。块主体允许lambda主体包含多个语句。这些语句包含在括号内,您必须在括号后添加分号。
注意 :对于块体,您应该始终有一个return语句。但是,表达式主体不需要return语句。
示例3:Lambda表达式
让我们编写一个Java程序,该程序使用lambda表达式返回Pi的值。
如前所述,lambda表达式不是单独执行的。相反,它形成了由功能接口定义的抽象方法的实现。
因此,我们需要首先定义一个功能接口。
import java.lang.FunctionalInterface;
// this is functional interface
@FunctionalInterface
interface MyInterface{
// abstract method
double getPiValue();
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
MyInterface ref;
// lambda expression
ref = () -> 3.1415;
System.out.println("Value of Pi = " + ref.getPiValue());
}
}
输出 :
Value of Pi = 3.1415
在上面的示例中,
- 我们创建了一个名为MyInterface的功能接口。它包含一个名为
getPiValue()
抽象方法。 - 在Main类内部,我们声明了对MyInterface的引用。请注意,我们可以声明接口的引用,但不能实例化接口。那是,
// it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
- 然后,我们为参考分配了一个lambda表达式。
ref = () -> 3.1415;
- 最后,我们使用引用接口调用方法
getPiValue()
。什么时候System.out.println("Value of Pi = " + ref.getPiValue());
带参数的Lambda表达式
到目前为止,我们已经创建了不带任何参数的lambda表达式。但是,类似于方法,lambda表达式也可以具有参数。例如,
(n) -> (n%2)==0
在此,括号内的变量n是传递给lambda表达式的参数。 Lambda主体接受参数并检查其是偶数还是奇数。
示例4:将lambda表达式与参数一起使用
@FunctionalInterface
interface MyInterface {
// abstract method
String reverse(String n);
}
public class Main {
public static void main( String[] args ) {
// declare a reference to MyInterface
// assign a lambda expression to the reference
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
// call the method of the interface
System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
}
}
输出 :
Lambda reversed = adbmaL
通用功能接口
到目前为止,我们使用的功能接口仅接受一种类型的值。例如,
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
上面的功能接口仅接受String
并返回String
。但是,我们可以使功能接口通用,以便接受任何数据类型。如果不确定泛型,请访问Java泛型。
示例5:通用功能接口和Lambda表达式
// GenericInterface.java
@FunctionalInterface
interface GenericInterface {
// generic method
T func(T t);
}
// GenericLambda.java
public class Main {
public static void main( String[] args ) {
// declare a reference to GenericInterface
// the GenericInterface operates on String data
// assign a lambda expression to it
GenericInterface reverse = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
System.out.println("Lambda reversed = " + reverse.func("Lambda"));
// declare another reference to GenericInterface
// the GenericInterface operates on Integer data
// assign a lambda expression to it
GenericInterface factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++)
result = i * result;
return result;
};
System.out.println("factorial of 5 = " + factorial.func(5));
}
}
输出 :
Lambda reversed = adbmaL
factorial of 5 = 120
在上面的示例中,我们创建了一个名为GenericInterface的通用功能接口。它包含一个名为func()
的通用方法。
在主班里面
-
GenericInterface
-创建对接口的引用。现在,该接口对reverse String
类型的数据进行操作。 -
GenericInterface
-创建对接口的引用。在这种情况下,该接口对factorial Integer
类型的数据进行操作。
Lambda表达式和流API
新的java.util.stream软件包已添加到JDK8中,它允许Java开发人员执行诸如搜索,过滤,映射,缩小或操纵Lists
集合之类的操作。
例如,我们有一个数据流(在我们的示例中为String
List
),其中每个字符串都是国家名称和国家/地区的组合。现在,我们可以处理此数据流,并且仅从尼泊尔检索位置。
为此,我们可以结合Stream API和Lambda表达式在流中执行批量操作。
示例6:演示将lambda与Stream API一起使用
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
// create an object of list using ArrayList
static List places = new ArrayList<>();
// preparing our data
public static List getPlaces(){
// add places and country to the list
places.add("Nepal, Kathmandu");
places.add("Nepal, Pokhara");
places.add("India, Delhi");
places.add("USA, New York");
places.add("Africa, Nigeria");
return places;
}
public static void main( String[] args ) {
List myPlaces = getPlaces();
System.out.println("Places from Nepal:");
// Filter places from Nepal
myPlaces.stream()
.filter((p) -> p.startsWith("Nepal"))
.map((p) -> p.toUpperCase())
.sorted()
.forEach((p) -> System.out.println(p));
}
}
输出 :
Places from Nepal:
NEPAL, KATHMANDU
NEPAL, 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表达式作为输入。
我们还可以根据上面学习的语法定义自己的表达式。如上例所示,这使我们可以大大减少代码行。