介绍
在本文中,我们将使用示例探讨Java中的lambda表达式,它们与函数接口,参数化函数接口以及Stream API的结合使用。
Lambda表达式是在Java 8中添加的。它们的主要目的是提高可读性并减少代码量。
但是在继续使用lambda之前,我们需要了解功能接口。
什么是功能接口?
如果Java中的接口仅包含一个抽象方法,则该方法称为函数。此单一方法确定接口的用途。
例如,来自java.lang包的Runnable接口是有效的,因为它仅包含一个run()方法。
示例1:在Java中声明功能接口
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
//
double getValue();
}
在上面的示例中,MyInterface接口只有一个抽象方法getValue()。因此,该接口是有效的。
在这里我们使用了注释功能接口这有助于编译器了解该接口是否起作用。因此,它不允许使用多个抽象方法。但是,我们可以忽略它。
在Java 7中,功能接口被视为单一抽象方法(SAM)。SAM通常使用匿名类来实现。
示例2:使用Java中的匿名类实现SAM
public class FunctionInterfaceTest {
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表达式具有以下语法:
(parameter list) -> lambda body
在这里,我们使用了一个新的运算符(->)-lambda运算符。语法似乎有些棘手。让我们看几个例子。
假设我们有一个这样的方法:
double getPiValue() {
return 3.1415;
}
我们可以使用lambda编写它,例如:
() -> 3.1415
此方法没有参数。因此,表达式的左侧包含空括号。右侧是lambda表达式的主体,它定义了其动作。在我们的例子中,返回值为3.1415。
Lambda表达式的类型
在Java中,lambda的主体可以为两种类型。
1.单行
() -> System.out.println("Lambdas are great");
2.块(多行)
() -> {
double pi = 3.1415;
return pi;
};
此类型允许lambda表达式在内部具有多个操作。这些操作必须用大括号括起来,后跟分号。
注意:与单行表达式不同,多行lambda表达式必须始终具有return语句。
示例3:Lambda表达式
让我们编写一个Java程序,该程序使用lambda表达式返回Pi。
如前所述,lambda表达式不会自动执行。相反,它形成了在功能接口中声明的抽象方法的实现。
因此,首先,我们需要描述功能接口。
import java.lang.FunctionalInterface;
//
@FunctionalInterface
interface MyInterface{
//
double getPiValue();
}
public class Main {
public static void main( String[] args ) {
// MyInterface
MyInterface ref;
// -
ref = () -> 3.1415;
System.out.println("Value of Pi = " + ref.getPiValue());
}
}
执行结果:
Value of Pi = 3.1415
在上面的示例中:
- 我们创建了一个功能接口MyInterface,其中包含一个抽象方法getPiValue()。
- 在Main类内部,我们声明了对MyInterface的引用。注意,我们可以声明一个接口引用,但是不能创建它的对象。
// MyInterface ref = new myInterface(); // 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 {
//
String reverse(String n);
}
public class Main {
public static void main( String[] args ) {
// MyInterface
// -
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length()-1; i >= 0 ; i--)
result += str.charAt(i);
return result;
};
//
System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
}
}
执行结果:
Lambda reversed = adbmaL
参数化功能接口
到目前为止,我们使用的功能接口仅接受一种值类型。例如:
@FunctionalInterface
interface MyInterface {
String reverseString(String n);
}
上面的功能接口仅接受String并返回String。但是,我们可以使接口通用,以与任何数据类型一起使用。
示例5:参数化的接口和Lambda表达式
//
@FunctionalInterface
interface GenericInterface<T> {
//
T func(T t);
}
public class Main {
public static void main( String[] args ) {
//
// String
//
GenericInterface<String> 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"));
//
// Integer
//
GenericInterface<Integer> 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()方法。
然后,在Main类中:
- GenericInterface <String>反向-创建指向使用String的接口的链接。
- GenericInterface <Integer>阶乘-创建指向与Integer一起使用的接口的链接。
Lambda表达式和流API
JDK8添加了一个新的包java.util.stream,该包允许java开发人员执行诸如搜索,过滤,匹配,连接或操纵诸如List之类的集合的操作。
例如,我们有一个数据流(在我们的例子中是一个字符串列表),其中每个字符串都包含一个国家及其城市的名称。现在,我们可以处理此数据流,并仅选择尼泊尔的城市。
为此,我们可以结合使用Stream API和lambda表达式。
示例6:在流API中使用Lambda
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
//
static List<String> places = new ArrayList<>();
//
public static List getPlaces(){
//
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<String> myPlaces = getPlaces();
System.out.println("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作为参数。
同样,我们可以根据上述语法描述自己的表达式。这将使我们减少代码行数。