在 Java 8 中重用 lambda 表达式
Reusing lambda expressions in Java 8
Java 8 允许您在不同的上下文中使用相同的 lambda 表达式。考虑以下演示这一点的代码:
public class LambdasTypeCheck {
public static void main(String []args) {
//Same lambda expression can be used in different contexts.
Predicate<List<String>> predicate = (l) -> l.add("1");
Consumer<List<String>> consumer = (l) -> l.add("1");
}
}
lambda 表达式(l) -> l.add("1");
在两个地方重复。我怎样才能避免这种重复?我可以重用这个 lambda 表达式的一种方法是,如果有一些 interface/class 它是所有 lambda 表达式的父级,这样我就可以将任何 lambda 表达式分配给这样一个 interface/class 的引用。
您没有定义您担心的是哪种“重复”。
如果是代码重复(或者说,担心故意应该相同的表达式之间可能存在不一致)困扰您,您可以使用
Predicate<List<String>> predicate = (l) -> l.add("1");
Consumer<List<String>> consumer = predicate::test;
避免它。
如果您希望 lambda 表达式真正由同一个实例表示,您可以创建一个组合 interface
:
interface PredicateAndConsumer<T> extends Predicate<T>, Consumer<T> {
public default void accept(T t) { test(t); }
}
PredicateAndConsumer<List<String>> pAndC = (l) -> l.add("1");
Predicate<List<String>> predicate = pAndC;
Consumer<List<String>> consumer = pAndC;
因此,如您所见,您可以在需要 Predicate<List<String>>
或 Consumer<List<String>>
的两种情况下使用 pAndC
。
但是,这样做的好处不大。在这种特殊情况下,我们可以安全地假设对于大多数 JRE,引入另一个 interface
的开销高于通过一个 lambda 表达式实现它的节省。
您也可以考虑“Is method reference caching a good idea in Java 8?”的答案。总而言之,您的示例表达式不捕获值,因此将在 Oracle 的参考实现中作为单例实现(并且可能在所有替代实现中也是如此)。所以这里要说的是在运行时环境中是否创建了一个或两个不可变常量…
让我们面对现实吧,使用相同代码在同一方法中实现两个不同接口的可能性几乎为零。但即便如此,您也可以执行遇到重复代码时可能经常执行的操作:创建一个新方法 / class.
public class LambdasTypeCheck {
public static void main(String []args) {
Predicate<List<String>> predicate = LambdasTypeCheck::addOne;
Consumer<List<String>> consumer = LambdasTypeCheck::addOne;
}
public static boolean addOne(List<String> list) {
return list.add("1");
}
}
当然,当您想捕获值时,这不起作用:
String one = "1";
Predicate<List<String>> predicate = (l) -> l.add(one);
Consumer<List<String>> consumer = (l) -> l.add(one);
这里必须使用lambda表达式,只能求助于@Holger建议的解决方案。但话又说回来:这(方法中的代码重复)是一种非常非常不可能的情况:)
Java 8 允许您在不同的上下文中使用相同的 lambda 表达式。考虑以下演示这一点的代码:
public class LambdasTypeCheck {
public static void main(String []args) {
//Same lambda expression can be used in different contexts.
Predicate<List<String>> predicate = (l) -> l.add("1");
Consumer<List<String>> consumer = (l) -> l.add("1");
}
}
lambda 表达式(l) -> l.add("1");
在两个地方重复。我怎样才能避免这种重复?我可以重用这个 lambda 表达式的一种方法是,如果有一些 interface/class 它是所有 lambda 表达式的父级,这样我就可以将任何 lambda 表达式分配给这样一个 interface/class 的引用。
您没有定义您担心的是哪种“重复”。
如果是代码重复(或者说,担心故意应该相同的表达式之间可能存在不一致)困扰您,您可以使用
Predicate<List<String>> predicate = (l) -> l.add("1");
Consumer<List<String>> consumer = predicate::test;
避免它。
如果您希望 lambda 表达式真正由同一个实例表示,您可以创建一个组合 interface
:
interface PredicateAndConsumer<T> extends Predicate<T>, Consumer<T> {
public default void accept(T t) { test(t); }
}
PredicateAndConsumer<List<String>> pAndC = (l) -> l.add("1");
Predicate<List<String>> predicate = pAndC;
Consumer<List<String>> consumer = pAndC;
因此,如您所见,您可以在需要 Predicate<List<String>>
或 Consumer<List<String>>
的两种情况下使用 pAndC
。
但是,这样做的好处不大。在这种特殊情况下,我们可以安全地假设对于大多数 JRE,引入另一个 interface
的开销高于通过一个 lambda 表达式实现它的节省。
您也可以考虑“Is method reference caching a good idea in Java 8?”的答案。总而言之,您的示例表达式不捕获值,因此将在 Oracle 的参考实现中作为单例实现(并且可能在所有替代实现中也是如此)。所以这里要说的是在运行时环境中是否创建了一个或两个不可变常量…
让我们面对现实吧,使用相同代码在同一方法中实现两个不同接口的可能性几乎为零。但即便如此,您也可以执行遇到重复代码时可能经常执行的操作:创建一个新方法 / class.
public class LambdasTypeCheck {
public static void main(String []args) {
Predicate<List<String>> predicate = LambdasTypeCheck::addOne;
Consumer<List<String>> consumer = LambdasTypeCheck::addOne;
}
public static boolean addOne(List<String> list) {
return list.add("1");
}
}
当然,当您想捕获值时,这不起作用:
String one = "1";
Predicate<List<String>> predicate = (l) -> l.add(one);
Consumer<List<String>> consumer = (l) -> l.add(one);
这里必须使用lambda表达式,只能求助于@Holger建议的解决方案。但话又说回来:这(方法中的代码重复)是一种非常非常不可能的情况:)