如何在Java中使用Lambda表达式和函数接口进行开发

您所在的位置:网站首页 接口的请求参数是什么 如何在Java中使用Lambda表达式和函数接口进行开发

如何在Java中使用Lambda表达式和函数接口进行开发

2023-04-27 22:30| 来源: 网络整理| 查看: 265

Java 中匿名类的问题是什么?

匿名类的问题是有太多的样板代码掩盖了实际代码,这个问题通过 lambda 表达式解决了。在这里,所有样板都被移除,只剩下重要的代码。 

例如,要按照前面的长度对字符串列表进行排序,我们必须像这样传递一个匿名比较器:

Collections.sort(list, new Comparator(){ public int compare (String s1, String s2) { return s1.length() - s2.length(); } }); 复制代码

您可以像这样使用 lambda 表达式来做同样的事情:

Collections.sort(list, (s1, s2) -> s1.length() - s2.length()); 复制代码

您可以看到,我们没有传递整个匿名类,而是传递重要的代码,即 

s1.length() - s2.length() 复制代码

这个例子对于理解 lambda 表达式和函数式接口究竟如何协同工作非常重要。 

需要注意到,这里我们传递的是 lambda 表达式而不是匿名类,这意味着您可以用 lambda 表达式代替匿名类,但仅限于某些情况。 

按照规则,如果方法接受功能接口,则可以将 lambda 表达式传递给该方法,现在什么是功能接口?只不过是一个只有一个抽象方法或单个抽象方法的接口,例如 Runnable、Comparable、Comparator、EventListener 等。 

这就是为什么人们说Java 中的 lambda 是 SAM 类型的原因,因为您只能将 lambda 表达式转换为 Java 中的函数式接口, 其他语言也支持函数式字面值,但 Java 中不支持。Java 中 lambda 表达式的唯一用途是将其转换为函数式接口。 

顺便说一句,除了所有现有的具有单一抽象方法的接口,例如 Runnable、Callable、Comparable,还有许多通用功能接口被添加到 JDK 的 java.util.function 包下,例如 Predicate、Supplier、Consumer、BiFunction 等。 

还有一个名为@functional的注释,用于将接口标记为功能性的,但这是可选的。这意味着什么?这意味着,如果您只是放置不会使任何接口成为函数式的 @Functional ,那么您的接口必须只有一个抽象方法才能成为函数式接口。

那这个@functional注解有什么用呢?好吧,它有两个目的,首先它检查你的接口是否真的只有一个抽象方法,即它确保它的功能,它还会生成 Javadoc 以表明你的接口是功能性的。

Java 开发人员应该学习的 5 个基本功能接口

回到java.util.fucntion包中一些流行的函数接口,这将帮助我们回答您的第二个问题,例如如何编写 lambda 表达式以传递给方法。 

*Predicate

该接口接受一个 T 类型的参数并返回一个布尔值,这意味着您可以使用它来指定导致布尔值的条件,例如价格 > 1000 等。 

Consumer

此功能接口接受一个参数并且不返回任何内容,即 void

Function

此功能接口接受一个 T 类型的参数并返回一个 R 类型的值。 

Suppoer

此功能接口不接受任何参数,但返回类型 T 的值,例如工厂方法。 

现在,假设您有一个接受 Predicate 的方法,然后您可以将 lambda 表达式传递给它,结果为 true 或 false。例如,Stream 的 filter() 方法接受一个 Predicate:

list.filter( s -> s.length() > 10 ) 复制代码

如果你仔细观察,lambda 表达式是:

s -> s.length() > 10 复制代码

其中 s 是参数,s.length > 10 是实际代码。这等于像这样的方法:

public boolean test (String s) { 返回s.length > 10 ; } 复制代码

因此,我们将函数或代码传递给方法,这就是 lambda 表达式也称为匿名函数的原因。

简而言之,如果一个方法接受一个功能接口,你可以向它传递一个 lambda 表达式。这就回答了我们的第一个问题。 

lambda 表达式的美妙之处在于它减少了样板代码,这就是为什么您不需要编写代码来创建方法,您只需编写实际的逻辑即可。Java 也会推断类型,这就是为什么我们没有指定 s 是否为 String,Java 会自动从上下文中知道它,但如果您从以下开始,您也可以这样做:

字符串 s -> s.length > 10 复制代码

稍后,一旦您获得了经验,您将只编写重要的代码并将所有内容留给 javac。 

编写 lambda 表达式的另一个重要事项是,您需要根据预期来编写它。例如,如果一个方法需要一个比较器,这意味着它需要一个带有两个参数并返回一个 int 的函数,还记得 compare() 方法的语法吗?

所以,你可以这样写:

(s1, s2) -> s1.length() - s2.length() 复制代码

因为我们有两个参数,所以我们在左侧使用括号,即 (s1, s2),如果只有一个参数,则可以省略。类似地,在-> (箭头运算符)的右侧, 我们编写了导致整数的代码,如果您的代码只是一个衬里,则无需指定 return 语句,lambda 会为您完成。 

因为,你知道 s1 和 s2 是字符串,你可以调用它们的length()方法,而不指定类型,Java 会自动推断类型。因此,在需要按长度比较 String 的地方,可以传递以下 lambda 表达式,例如 

Collections.sort(list, (s1, s2) -> s1.length() - s2.length()); 复制代码

您可以看到,它更加简洁明了。 

现在,让我们再举一个 lambda 表达式的例子,这次有点复杂。如果您阅读过我关于在 Java 中合并地图的文章,那么您就会知道在 JDK 中的 java.util.Map接口中添加了一个 merge() 方法。

如果键来自两个映射类,即当您有重复键时,此方法使您可以灵活地决定值。这是通过传递一个重映射函数来完成的,该函数是一个名为BiFunction的函数式接口。 

此功能接口采用两个参数 T 和 U 并返回 R 类型的对象。现在,如果你有两个 Map 的整数和字符串,例如Map并且你想合并它们,你可以编写这样的代码:

for (Entry e : otherMap.entries()){ map.merge(e.getKey(), e.getValue(), (v1, v2) -> (v1 + ":" + v1)); } 复制代码

这里发生了什么?好吧,在相同键的情况下,调用重新映射函数是:

(v1, v2) -> (v1 + ":" + v1) 复制代码

如果你仔细观察你会发现 lambda 有两个参数并且它返回一个对象,这意味着你可以将它转换为一个 Bifunctional 函数接口,这实际上意味着你可以将它传递给 merge() 方法。 

两个参数 v1 和 v2 是 String 类型,但您不需要指定它们,它们是来自第一个和第二个映射的值,您所做的只是连接它们 (v1 + ":" + v1)。

简而言之,您如何编写 lambda 表达式取决于您要将其转换为哪个函数式接口,以及您传递 lambda 表达式的方法。如果方法接受一个 Comparable,这意味着您可以传递一个接受两个参数并返回一个 int 的 lambda。 

如果它是事件侦听器,那么您可以传递一个具有一个参数的 lambda 并且不返回任何内容,例如它只是打印一条语句或什么也不做。如果它是一个 BiFunction 那么你可以传递一个接受两个参数的 lambda 并返回第三个对象。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3