微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 Java 中将函数式接口声明为变量可能有哪些缺点?

如何解决在 Java 中将函数式接口声明为变量可能有哪些缺点?

我已经在 J​​ava 中尝试函数式编程一段时间了,并注意到我开始更喜欢使用 val result: Option[MyEntity] = if (option.isEmpty) { Some(Entity("X")) } else { option.flatMap { flagValue => if (flagValue) { None } else { Some(MyEntity("Y")) } } } 包中的 @FunctionalInterface 函数,例如 Functions、BiFunctions、UnaryOperators、 Predicates、BiPredicates 等,而不是我的类中的简单私有方法。我知道他们的应用程序更推荐作为参数传递给另一个函数,这就是我通常倾向于使用它们的方式,但我现在发现它们直接并且以某种方式更好。

事实上,我现在倾向于将其中一些声明为变量,然后在需要时在我的类中使用。

我似乎没有找到任何使用这些方法而不是简单方法的指南或缺点。

那么:以这种方式使用它们有什么缺点吗?

  • 示例:

为什么喜欢:

java.util.function

代替:

private boolean foo(final int a,final int b){
    return a < b;
}

我在最近的项目中如何使用它们的示例:

private final BiPredicate<Integer,Integer> foo = (a,b) -> a < b;

解决方法

您应该做最易读和最可维护的事情。如果将这些函数放在具有描述性名称的变量中感觉像是解决问题的一种可读且可维护的方式,那很好。这样的编程没有错。

您可能还喜欢的一个折中方法是将逻辑放在普通的静态方法中:

private boolean foo(final int a,final int b){
   return a < b;
}

然后在需要时使用方法引用来引用它:MyClass::foo。这在行为上等同于您定义的 lambda。

有很多方法可以编写此代码。每个人都对“正确”的方法有自己的看法,但实际上有很多“正确”的方法。 (还有一些不太正确的方法。)

,

决定此类事情的高级标准应该是正确性、可读性和性能......根据项目进行适当加权。

问:那么我们如何在这里“应用”这些标准?

答:很难...

问题在于,选择 lambdas 版本的函数引用在实现级别太低了,其影响几乎可以忽略不计。 (即使评估该级别的效率也可能无关紧要......除非在极端情况下。)

如果我们回到更广泛的功能(例如使用流)与经典(例如使用循环)的问题,仍然很难做出一般性建议: >

  • 正确性:函数式风格通常更容易推理,但这并不适用于所有计算问题。 (例如,对于需要变异的有效解决方案的问题。)

  • 可读性:对于简单的问题,函数式/流解决方案通常更具可读性。然而,情况并非总是如此。有时,功能性解决方案必然令人费解。有时,功能解决方案可能会不必要地抽象化,以试图在一系列问题或子问题中强加通用模式。

  • 效率:这里也没有通用的答案。一方面,优化好的代码/解决方案的性能差异很小。另一方面,很容易天真地使用流(或经典方法)。并且给定方法的(低)效率很容易被过度使用抽象掩盖......


关于您的示例代码:作为阅读和维护该代码的人,我主要担心的是很难理解它的实际作用。也许你(作者)理解它(现在)。但有人感冒会挣扎。 (具体来说,>>我

元问题是,客观地评估可读性是非常困难的(并且非常昂贵1)。所以我们只剩下主观评价;即只是人们的意见。

另一个问题是,如果这是游戏算法(“AI”玩家)的一部分,那么效率可能是项目成功的关键。 (假设这是一个严肃的项目。)

但另一方面,如果这段代码只是你“实验”,那么其他人的想法,无论他们是否理解代码,或者它是如何执行的,都是没有实际意义的。


一些思考......

这与旧的自上而下与自下而上的设计争论有关。这两种方法都可以工作。这两种方法都可能失败。自下而上的失败模式之一是倾向于构建一堆复杂的抽象基础设施,“因为你认为你会使用它”。然后当你真正开始使用它时,你可能会发现它要么是不对的......要么你可以在没有抽象的情况下更简单地完成它。

我还注意到,一些聪明的程序员经历了开发自己的实用程序类或框架或其他任何工具包的“阶段”……他们认为他们将能够在未来的项目中使用它们。 (很难说是什么导致了这种情况,但它可能源于无聊。)无论如何,它不能很好地扩展,无论是时间还是团队规模。这些工件倾向于变成团队不喜欢的遗留“残骸”,并且(最终)让作者后悔。特别是如果作者有胆量发布工件并鼓励其他人使用它。 (GitHub 上又一个死掉的项目!)


1 - 您需要设置一个实验,让一群有经验的程序员针对同一问题提供(例如)两种不同的解决方案,他们需要阅读并回答有关它的问题,或修改它,或者其他的东西。然后针对不同的问题重复该过程。

,

众所周知,Java 是一种相当古老的编程语言(在某种程度上),它定期更新以在可供选择的编程语言方面保持领先地位。话虽如此,您可能一直在阅读解释接口的旧文档。

private boolean foo(final int a,final int b){
return a < b;

}

是一个有效的函数,您可以在很多文档和学习接口的基础知识中看到它。

现在您正在比较处理方法/接口的“旧”方式和“新”得多的方式。随着 Java 8 的发布,他们添加了 lambda 表达式,我相信这是为了让程序员能够以“更少”的行数和更快的方式(除其他外)编写方法。您希望您的编程语言在安全性方面与其他语言保持同步。与 Python 相比,Java 是一种笨重的编程语言。

private final BiPredicate<Integer,Integer> foo = (a,b) -> a < b;

这也是用 Java 8 和任何更新版本编写的可接受的方法/函数。以这种方式编写函数没有问题,并且可能会更快地编写代码。

以这种“较新”的方式编写方法的唯一问题是

  1. 您使用的是 lambda,有些人可能没有使用 lambda 及其功能的经验
  2. 有人试图阅读您的代码,可能无法理解它

它没有任何问题,只有当您所在的团队说“不,您不能使用 lambdas”时才首选“旧”方式。没有区别。

最后一点,与匿名类相比,您可能会发现 lambda 速度的基准测试很有趣,这可以找到 here

[编辑] 我想补充一点,只为简单函数编写 lambda,我知道它们非常强大,但是编写 lambda 来搜索列表然后传统的 for 循环没问题,但复杂的方法应该以清晰的方式编写.这只是为了避免在调试程序时头疼。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。