【问题标题】:How to program without side-effects in Java?如何在 Java 中编程而不产生副作用?
【发布时间】:2012-01-13 05:39:41
【问题描述】:

作为一名长期的 Java 程序员和近年来对 Haskell 的痴迷,我现在正在学习 Scala。我的问题是:

如何在 Java 中编程而不产生副作用?

即我怎样才能手动完成 Scala 为我做的事情?

Odersky 的 Scala 书经常用 OO 术语解释函数概念(例如 val = final,if = ternary op.)。

是否有可访问的指南如何在 Java 中进行编程而不产生副作用(尽可能少)?我想一个人可以通过简单的旧 java 和一些纪律(例如,不可修改的集合、最终值、不修改对象/应用程序状态的严格方法)获得许多已知的好处。

【问题讨论】:

  • 这取决于你所说的副作用。在做一些未记录的事情方面,大多数 Java API 没有副作用。
  • @PeterLawrey :我认为 OP 意味着 purity 意义上的副作用。
  • Neal Ford 在 IBM developerWorks 上的一些想法:ibm.com/developerworks/views/java/…:
  • 这篇博文可能会让你感兴趣:etorreborre.blogspot.com/2011/12/pragmatic-io.html
  • 从并发的角度来看,您可以使用基于 Scala 的解决方案 Akka 应用基于参与者的并发,该解决方案可用于许多不同的 JVM 语言。

标签: java scala functional-programming


【解决方案1】:

一种没有副作用的编程方法是只使用immutable 类。 您可以将它应用到您自己的类中,但如果不从头开始重写,就无法更改标准 Java 运行时。

无论如何,让 Java 没有副作用是没有意义的,因为它是一种具有 OO 数据封装的命令式编程语言。换句话说,副作用是语言的一部分,本身并不是邪恶的。

【讨论】:

  • 是什么让一个类“不可变”?
  • @Bastl:例如它的所有属性都是不可变的......在Java中,如果你的属性是例如,它就会成为一个问题。本机数组。
  • @home: 参数传递和像 println() 这样的“动作”呢?
  • @Bastl:参数传递:如果所有参数本身都是不可变的,这不是问题。 println 不是一个很好的例子,至少它修改了底层输出流。
  • @Bastl 我认为是 Odersky 本人说过纯函数式程序只能产生热量。 I/O 本质上是没有功能的,这就是为什么它与 Haskell 中的常规代码与 monad 分开的原因。如果 Java 对象在实例化后没有可以更改其引用的字段并且它可以到达的所有对象都是不可变的,那么它就是不可变的。一个很好的例子是字符串。强制集合的一个好方法是 Collections.unmodifiableList() 等。
【解决方案2】:

【讨论】:

  • 我想这就是我想要的。我仍然希望有一些类似于 java 的“属性始终是私有的,getter/setter 是公共的”或常量是“public static final Type CAPITALS = ...”的拇指规则。我使用大量遗留的 Java 代码,并希望重用我的 FP 能力并与我的同事分享它们。但我看到下面的讨论还在继续:-)
【解决方案3】:
  1. 将每个方法、字段和类设为abstractfinal
  2. 使每个变量和方法参数final
  3. 如果您使用可变结构(如 Array)或 i/o 资源(如文件和数据库连接),切勿让它们超出构造它们的范围。

【讨论】:

    【解决方案4】:

    作为一名长期的 Java 程序员和近年来对 Haskell 的痴迷,我现在正在学习 Scala。

    恭喜:)

    如何在 Java 中编程而不产生副作用?即,我怎样才能手动完成 Scala 为我做的事情?

    好吧,就强制或协助无副作用的风格而言,Scala 并没有为您做太多事情(与 Haskell 相比)。例如,在 Scala 中,您始终可以选择在任何地方使用 var,而在 Haskell 中,如果您想要可变的 IORefSTRef,则需要使用适当的 monad 向类型系统指出这一点。

    换句话说,虽然您问的是如何在 Java 中模仿 Scala 风格,但首先要考虑的是如何在 Scala 中模仿 Haskell 风格,这当然是 Scalaz 发挥作用的地方。

    说了这么多,我想你已经回答了你自己的问题:

    我想人们可以通过简单的旧 java 和一些纪律(例如,不可修改的集合、最终值、不修改对象/应用程序状态的严格方法)获得许多已知的好处。

    纪律至少是故事的一半。简单地使用 final 和不可变集合 (à la Guava) 会对您的代码最终的样子产生各种影响。当然,故事的另一半是当您的程序需要副作用(例如 I/O)时,您会怎么做?在 Haskell 中你会使用 IO monad,而在 Scala 中你可能会选择使用 scalaz.effects.IO,但在 Java 中,老实说,我认为这不值得一战。

    在我看来,您将在任何 Java 程序中编写的最重要的两行函数式代码如下:

    import static com.google.common.collect.Iterables.filter;
    import static com.google.common.collect.Iterables.transform;
    

    剩下的就是给读者的练习;)

    【讨论】:

    • 你的意思是什么后果? “简单地使用最终的和不可变的集合(à la Guava)会对你的代码最终的样子产生各种影响。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-29
    • 2016-02-05
    • 1970-01-01
    • 2022-07-16
    • 2011-04-10
    • 2013-07-06
    • 1970-01-01
    相关资源
    最近更新 更多