首页 > 编程知识 正文

第一范式函数依赖(java函数调用)

时间:2023-05-03 05:45:27 阅读:88540 作者:2761

在范式和技术堆栈不断变化的世界里,保持竞争力、提高生产力和质量的斗争有时被证明是挑战。

本文首先展示了函数编程(FP )的优越性,特别希望加强Java编码体验。 当我尝试将范式转换为函数型编程时,我将重复几个最重要的理由。 请记住,这绝不是一个大的创新。 我相信FP自70年代以来一直存在,但只在这几年获得了吸引力,增加了人们的兴趣。 让我们看看为什么!

随着

并发

多核/多线程处理器的出现,函数型编程变得更加引人注目。 这绝不是简单的巧合。 函数式编程鼓励使用不可变对象,属性和变量必须是不能更改值的数据容器。)。 请看以下代码:

私有inta编号;

公共编号参数{1} {2}

this.a编号=编号参数;

}

很简单吧? 你以前可能看过很多次。 但是,如果两个线程同时访问setNumber方法会怎么样? 可以想象可能会发生某种阻滞。 最后,只有访问此方法的最后一个线程对aNumber的值有最终决定权。 但是,这是不确定的,取决于各种因素,因此可以说方法setNumber不是引用透明的(详细情况将在后面叙述)。 在这种情况下,不变性有助于推理代码。 因为我确信无论线程访问多少其一部分,其值始终相同。

的透明度和可测试性

在函数型编程中,推荐使用引用透明函数。 那是什么意思? 是的,这意味着函数总是被其值取代,一切都不会改变。 让我们看看下面的代码块。

导入Java.util.random;

publicclassrandomvalueprovider {

公共int几何随机值(

随机随机=新随机(;

returnrand.nextint(50;

}

}

getSomeRandomValue ()方法的引用是透明的吗? 请试着换成那个值。 那个总是没有变化吗? 可能做不到。 尽可能地尝试使用参考透明函数可能是个好习惯。 想象一下,测试上面的getSomeRandomValue方法比测试下面的方法要困难得多。

公共获取(内部,内部b ) {

返回甲乙;

}

具有隐含名称的小函数通常优于表示返回值的表达式。 好处是,我们可以保证我们建立的(至少大部分)函数是确定的。 这样可以提高代码推理的方便性和测试性。

应用

函数组合

原则后,操作变得更简单,更可靠。 根据这个事实,通过组合各种功能,可以创建更复杂的行为。 将其他函数作为参数或返回函数一起接收的函数称为高阶函数。

下面的一些示例来自Java 8流API。 2014年成为JDK的一部分以来,已经在流中写入了大量的内容。 现在,我想用Consumer函数界面给大家举个简单的例子:

publicvoidprocesslistofnumbers (监听器集成处理器) {

返回列表编号. stream (

. foreach (编号处理器.接受)编号;

}

客户端代码:

listintegernumbers=arrays.as list (5,6,7,8 );

consumerintegernumberprinter=n-system.out.println (

处理序列号(编号,编号打印机);

方法processListOfNumbers是函数组合的示例,有时也称为高阶函数。 在Java中,函数(包括suppliers、consumers )是对象。 这意味着可以应用它们,然后将其组合起来作为参数传递。

以FP风格编写的应用程序更加强大

在用函数表达式编写代码时,不容易发生APP本身的错误。 这是因为移动组件可以使APP更容易预测,更容易推理,更能适应逆境。 函数组合和不变性的常见用法确保了由于APP的不同部分的状态发生变化而导致的所有错误在缺省情况下消失

。该应用程序将更加强大,可以提供更短的开发 - >测试 - >调试迭代循环。

专注于“什么”而不是“如何”

假设我们有一个getUserById方法(在同一个类中)负责从数据库中获取相应的User对象,请使用以下Java流的经典应用程序:

public List<User> getAdultUsers(List<Integer> listOfUserIds) {

return listOfUserIds.stream().map(this::getUserById)

.filter(user -> user.getAge() >= 18)

.collect(Collectors.toList());

}

现在让我们看看非函数风格的相同代码:

public List<User> getAdultUsers(List<Integer> listOfUserIds) {

List<User> adultUsers = new ArrayList<>();

for(int id: listOfUserIds) {

User user = getUserById(id);

if (user.getAge() >= 18) {

adultUsers.add(user);

}

}

return adultUsers;

}

除了第二段略长外,我们还可以注意到这段代码需要花时间来“解释”此操作的每个步骤是如何完成的:创建一个空白列表,迭代id,获取每个用户,添加一些基于条件表达式的用户到空白列表,完成并返回收集的用户。

另一方面,在第一段中,功能方法更侧重于“什么”。代码在做什么?它将一些ID映射到某些用户,将其过滤掉并将其余用户收集到列表中。有人可能会争辩说,通过在第二种情况下提取小方法可以实现同样的目的,但我相信第一段的流和函数作为数据方法仍然更好。它将我们的函数置于业务逻辑的最前沿,具有与在我们的应用程序中移动的任何其他数据相同的状态。

更好看的方法签名

当我们的功能从命令式转变为函数式时,命名也一目了然,以下方法很难通过其签名来阅读:

public void executeProcess() {

// executing some mysterious stuff!

}

代码做了什么?为什么它不想要我们的任何输入参数,为什么它不想返回任何结果?你能测试一下吗?你能读懂吗?不容易吧。如果像下面这样看起来如何?

public ExecutionStatus executeProcess(Process processToBeExecuted) {

// execute "processToBeExecuted" and return some status

}

只需采用一些FP概念,并在这个简单的情况下使用它们,代码就变得更具可读性。函数现在是可通过查看它的方法签名来说明自己(虽然方法名称可能仍然可以改进)。它需要一个Process输入并以某种ExecutionStatus状态返回。除了直接在代码中提供更好的“文档”之外,签名变得更有意义。执行什么Process?我们可以查看Process对象并在运行时查看它。它发挥作用后会发生什么?我然后可在我们的流程中使用该函数的返回结果。

结论

如今,无论我们是在处理遗留代码还是新建绿地项目,我们都可以使用一些东西来提高日常工作的质量和生产率。函数编程从不同的角度进行编码。它通常意味着更简洁,但如果给予适当的照顾,也会提高可读性。它还帮助我们解决一些常见的痛苦,例如并发编程中的竞争条件,老实的保温杯对象状态错误或难以遵循的代码。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。