纯方法必须是静态的吗?

Posted

技术标签:

【中文标题】纯方法必须是静态的吗?【英文标题】:Does a pure method have to be static? 【发布时间】:2018-11-01 03:18:54 【问题描述】:

我目前正在阅读一本教科书,该教科书将纯方法定义为:

“一个静态方法,只依赖于它的参数而不依赖其他数据”

实例方法不可能是纯方法而不是静态的(只要它不修改参数并且没有像打印这样的“副作用”)?

我知道纯方法是纯方法,返回值仅取决于参数而不取决于任何其他状态,因此调用实例方法的方式可能意味着从调用方法的对象中获取的变量不' t 算作参数,但算作另一个“状态”?

除此之外,我想不出非静态方法不能成为纯方法的任何原因。

这是一个例子:

public class Rational 

    private int numer;
    private int denom;

    public Rational() 
        this.numer = 0;
        this.denom = 1;
    

    public Rational(int numer, int denom) 
        this.numer = numer;
        this.denom = denom;
    

上面定义了一个Rational

然后您可以在Rational 类中编写一个方法,该方法通过下面的“方法一”或“方法二”将Rational 对象作为double 返回。

方法一:

public double toDouble() 
    double x = this.numer;
    double y = this.denom;
    double fprat = x / y;
    return fprat;
   

方法二:

public static double toDouble(Rational rational)

    double x = rational.numer;
    double y = rational.denom;
    double fprat = x / y;
    return fprat;
 

它们本质上做的事情完全相同,但一个是静态方法,另一个是实例方法,因此它们的调用格式不同。方法二当然是纯方法,但是在这种情况下,非静态的方法一也可以定义为纯方法吗?

【问题讨论】:

我想是的。如果您在 Java 类中有一个“纯”静态方法,那么您只需删除 static,它的依赖关系理论上不会改变。 如果从架构的角度来看,将纯方法作为实例方法是不合逻辑的。它是纯粹的,因为它没有状态并且是幂等的,所以它可能应该是一个静态方法 如果方法不是静态的,那么您将传递一个隐式的this 参数。并且参数的要求也必须适用于隐式 this 参数。 谁能提供一些建议,告诉我如何编辑这个问题而不是“主要基于意见”?我试图询问常见 Java 术语的基本定义,而不是人们对它们的看法。到目前为止,我感谢所有答案,并继续自己寻求澄清。我目前正在阅读有关纯方法和构造函数的 JML 部分。如果有人感兴趣,你可以在这里找到。 eecs.ucf.edu/~leavens/JML/jmlrefman/jmlrefman_7.html#SEC60 @Yassin 绝对允许纯方法接受状态作为输入(否则将毫无意义)。为什么隐藏的 this 指针应该被认为比函数的任何其他参数更特殊?这个论点导致了一个矛盾:假设public static int pureFunc(MyInstance self) 是纯的,显然同构public int pureFunc() 也是纯的。 【参考方案1】:

“纯”的基本意思是“只依赖于它的论点,没有副作用”。

静态方法不必是纯的,因为它可能会访问静态数据结构。同样,实例方法可能是纯的,因为它不访问任何实例变量。

所以“静态”和“实例”的概念与“纯”的概念没有直接关系。

【讨论】:

它可能访问静态数据结构”和“它不访问任何实例变量”也与纯度无关? 【参考方案2】:

纯方法也可能是静态的。纯方法的两个标准是,according to Wikipedia:

    该函数总是计算相同的结果值给定相同的 参数值。 函数结果值不能依赖于任何 隐藏信息在程序执行时可能会改变的状态 进行或在程序的不同执行之间,也不能 依赖于来自 I/O 设备的任何外部输入。

    结果的评估不会导致任何语义上可观察的 副作用或输出,例如可变对象或输出的突变 到 I/O 设备。

(强调我的)

这没有理由不能应用于非静态方法。将方法设为静态将为调用者提供更强的保证,即没有使用任何实例状态,因此将其设为静态可能会更好。


根据您的观点,可以将实例方法简单地视为一个带有额外隐式参数的函数:实例本身。通过这种方式,非静态方法可以依赖于实例状态并被视为纯方法,前提是它不依赖任何外部状态(单例等)或产生副作用。 Robert's answer puts it nicely。这取决于解释,但我个人认为这样的方法是纯粹的。

Voo 把它放在 cmets 中很好:

为什么隐藏的 this 指针应该被认为比 函数的任何其他参数?该论点导致 矛盾:假设public static int pureFunc(MyInstance self) 是纯的,显然同构public int pureFunc() 将是纯的 好吧。

【讨论】:

另一方面,如果我们想避免实例状态影响是纯的,那么函数没有理由不是静态的,因为它不会访问任何实例 这似乎依赖于许多语言隐藏传递给实例方法的 this 指针这一事实。但是忽略该约定,我不明白为什么应该处理 this 指针而不是该方法的任何其他输入。实际上,根据该定义,只要方法不改变成员变量,访问成员变量也是完全可以的! @Voo 这将失败条件 1。如果对象发生变异,则该方法可能会返回不同的值。所以:是的,您可以访问实例变量只能访问那些不可变的。这保证了无论对象如何使用 a.method(some, arg) 将始终返回相同的值。 @Bakuriu 我想说这取决于解释。如果我们说在实例上调用函数与以该对象作为隐式参数调用函数相同,我认为在状态改变后调用相同的函数是不是使用“相同的参数值”调用该函数。 根据这个定义,OP发布的两个方法都不是纯的,因为其他线程可能会在方法执行时更改Rational,并且Java的内存模型不能保证效果是否直接可见与否【参考方案3】:

该定义的“静态”部分是多余的。 static 的方法不能保证它不依赖任何其他状态。我怀疑定义只是想确保该方法不使用实例变量。

另一方面,从技术上讲,您也可以将实例方法视为具有第零个参数的静态方法,即对象本身。如果该对象是不可变的(或者该方法不会改变对象中的任何内容),您可能会争辩说该方法仍然是纯的。基本上您将“对象”视为附加参数。

例如,Java 中的Method references 可以这样做,使第一个参数成为调用方法的对象本身。

【讨论】:

我认为可变性与您的第二段无关。只有两个相等的实例(a==b)产生相同的结果a.f(x) == b.f(x)(并且没有副作用) 可以争辩吗? 应该争论。 @RobertBräutigam 如果您将实例视为函数的隐式参数,那么如果该实例在调用之间发生变化,那么您不会使用与以前相同的参数调用该方法。因此,它仍然可以是纯的并且是可变的。 @Bergi 同意。我认为没有太多理由认为传递给实例的 this 参数比任何其他参数都更特殊。或者如果隐藏参数真的改变了含义 - 为什么传递给静态方法的隐藏类指针也不是问题?只是没有多大意义。 @JoshHardman 纯方法必须是静态的吗?不。如果您接受对象本身(Java 中的“this”)是一个参数,而不是“隐藏”状态,那么如果实例方法不更改任何内容且不执行 IO,则它可以是纯的。根据 Wikipedia 对 pure (en.wikipedia.org/wiki/Pure_function) 的定义,该对象也必须是不可变的,正如我在回答中所说的那样。【参考方案4】:

这个方法:

public int add(int a, int b) 
   return a + b;

不是静态的,但它的返回值不依赖于除了参数之外的任何东西。但正因为如此,add 根本不需要是实例方法!

我认为这就是您的教科书所要达到的 - 不访问状态或改变状态的实例方法也可能是静态的。

实例方法应该依赖于对象的状态,否则它应该是一个静态方法。不依赖于静态的静态方法是纯粹的。

【讨论】:

你是对的,但我认为教科书并没有说这种方法“也可以是静态的”。我认为教科书说纯方法必然是静态的,没有考虑这种情况。【参考方案5】:

从概念上讲,静态方法和实例方法的唯一区别是实例方法有一个隐藏参数,可通过this 关键字访问。

因此,如果不改变 this 的实例方法符合纯静态方法的条件,那么它就是纯方法。

这里的问题可能与static 阻止的虚拟调度有关。基类方法可能是纯的,而派生类方法可能是不纯的,而它们在 Java 类型系统中的契约是相等的。

【讨论】:

我什至要补充一点,有多个隐藏参数。【参考方案6】:

不能保证static 方法是pure。从技术上讲,静态方法可以具有静态状态。 这就是为什么你的假设

"一个只依赖于它的参数而不依赖其他数据的静态方法"

不是真的。

编辑 在Bergi评论后

正如我之前所说,static 关键字并不能保证无状态。 如果您在引用中省略了 static 关键字,它仍然是正确的。

所以回答你的问题。纯方法必须是静态的吗?答案是视情况而定。

将纯函数设为静态有好处,即:

这是一种自检,可确保您不会(意外)更改班级中的状态;

无法覆盖该方法。这保证了该方法将来不会改变状态。

但另一方面,模拟静态方法很困难,并且在测试过程中可能会出现问题。

【讨论】:

这句话并不是说所有的静态方法都只依赖于它们的参数而不依赖于其他数据。它开始一个句子,讨论静态方法,这些方法只依赖于它们的参数而不依赖于其他数据。 @Bergi 也许我应该改写一下,我的意思是不能保证静态方法是纯的。 我明白你的意思。但这不是引用的意思。

以上是关于纯方法必须是静态的吗?的主要内容,如果未能解决你的问题,请参考以下文章

网站URL静态化是必须的吗?

在Java中,类中的枚举类型是静态的吗?

聚集索引必须是唯一的吗?

Qt ObjectName()必须是唯一的吗?

JSONP 对您的 API 来说是必须的吗?

抽屉通知图标必须是白色的吗?