lambda 表达式与静态方法
Posted
技术标签:
【中文标题】lambda 表达式与静态方法【英文标题】:lambda expression vs static method 【发布时间】:2017-10-30 01:27:32 【问题描述】:我有一个关于没有代码重复的 lambda 表达式的可重用性的问题。例如,如果我有一个辅助方法,我可以轻松地将其编码为静态方法,并且可以从其他类中引用它而无需重复代码。这在 lambda 表达式中如何工作? 示例:我编写了以下静态方法
public class MyUtil
public static int doubleMe(int x)
return x * 2;
我可以在整个项目的多个地方重复使用相同的方法,而无需重复代码
public class A
public void someOtherCalculation()
MyUtil.doubleMe(5);
public class B
public void myCalculation()
MyUtil.doubleMe(3);
当涉及到 lambda 函数时,它是如何工作的,编写一次函数并在多个类中使用相同的函数。
Function<Integer, Integer> doubleFunction = x -> x * 2;
在我的示例中,我将在哪里编写上述 lambda 函数以及如何在 A 类和 B 类中重用相同的函数?
【问题讨论】:
“代码重用”在这里是一个错误的目标。需要代码表现力。使用不“重用”代码的 lambda 有什么可能的危害,你有什么证据表明它没有?当许多不同的函子可以在算法中替代时,Lambdas 是适用的。你会为所有这些都写一个显式的静态方法,并为每个方法创建一个方法引用,这样你就可以说,“看,妈妈,我正在使用 lambdas!”?在这种情况下按惯例使用 lambda 不是更有意义吗?您寻求的真正工程效益是什么? 如果要复用一个函数,那么定义一个实现Function接口的类。静态方法不是函数。 【参考方案1】:上面的 lambda 函数在哪里写
由于您的函数没有引用任何字段,因此将其放在静态最终字段中是合适的:
class Utility
public static final Function<Integer,Integer> doubleFunction = x -> x * 2;
如何在 A 类和 B 类中重复使用相同的内容?
您可以将其称为Utility.doubleFunction
,并在需要它的上下文中传递它:
callMethodWithLambda(Utility.doubleFunction);
请注意,方法引用可以让您定义一个函数,并像使用 lambda 一样使用它:
class Utility
public static Integer doubleFunction(Integer x)
return x*2;
...
callMethodWithLambda(Utility::doubleFunction);
这种方法非常灵活,因为它允许您在多个上下文中根据需要重用相同的代码。
【讨论】:
【参考方案2】:确实,匿名函数适用于代码重用必要的情况。
愚蠢的例子,但假设您使用map
为列表中的每个数字添加两个。如果这是您可能到处都需要的常见操作,那么将数字加二的静态函数比在任何地方编写相同的 lambda 更有意义。
但是,如果您有一个将两个添加到列表中的函数,则将“添加两个”函数在本地定义为 lambda 会更有意义,这样您就不会使用其他任何地方都不需要的代码来插入您的类.
在编写大量使用高阶函数的 Clojure 时,我创建本地匿名函数来整理我正在编写的“完整”函数中的代码是很常见的。这些匿名函数中的绝大多数在“全局”范围(或类范围)中都是无意义的;特别是因为它们通常对局部变量有闭包,所以它们无论如何都不能是全局的。
【讨论】:
我一直假设这一点,但从未在任何地方阅读过。很有帮助。【参考方案3】:使用 lambda 表达式,您无需担心可重用性(事实上,大多数 lambdas 根本没有被重用)。如果你想要一个 Function
指针指向这个方法,你可以像下面这样声明:
Function<Integer, Integer> doubleFunction = MyUtil::doubleMe;
并将其传递给任何方法或流以应用/映射,例如:
public static void consume(Function<Integer, Integer> consumer, int value)
System.out.println(consumer.apply(value));
public static void main(String[] args) throws Exception
Function<Integer, Integer> doubleFunction = MyUtil::doubleMe;
consume(doubleFunction, 5);
【讨论】:
【参考方案4】:与其他答案不同。我想用TDD的方式回答你的问题。
如果您的doubleMe
就像您写的那样简单,那就是您应该停止滥用方法表达式引用,直接将其作为通用方法调用来调用。
如果您的doubleMe
非常复杂,以至于您想测试doubleMe
独立,您需要通过依赖注入显式地制作隐式依赖,以测试它们是否可以通过它们的通信协议一起工作。但是java不能直接引用方法,除非您使用反射apiMethod/使用实现SAM接口的匿名类,该接口将请求委托给jdk-8之前的方法。令人高兴的是,您可以将方法表达式引用引用到 jdk-8 中的功能接口。所以你可以通过使用函数式接口使隐式依赖显式,然后我想写一些通信协议测试如下:
@Test
void applyingMultiplicationWhenCalculating???()
IntUnaryOperator multiplication = mock(IntUnaryOperator.class);
B it = new B(multiplication);
it.myCalculation();
verify(multiplication).applyAsInt(3);
AND 那么你的类如B
应用的依赖注入更像如下:
public class B
IntUnaryOperator multiplication;
public B(IntUnaryOperator multiplication)
this.multiplication = multiplication;
public void myCalculation()
multiplication.applyAsInt(3);
那么您可以通过将方法表达式引用引用到功能接口来重用方法,如下所示:
A a = new A(MyUtil::doubleMe);
B b = new B(MyUtil::doubleMe);
【讨论】:
【参考方案5】:您可以执行以下操作。
class Fn
public static final Function<Integer, Integer> X2TIMES = x -> x *2;
class Test
public static void main (String[] args)
System.out.println(Fn.X2TIMES.apply(5));
【讨论】:
以上是关于lambda 表达式与静态方法的主要内容,如果未能解决你的问题,请参考以下文章
Java8函数式接口/Lambda表达式/接口默认方法/接口静态方法/接口冲突方法重写/lambda表达式指定泛型类型等