为啥将静态方法视为方法?

Posted

技术标签:

【中文标题】为啥将静态方法视为方法?【英文标题】:Why is a static method considered a method?为什么将静态方法视为方法? 【发布时间】:2015-08-04 19:11:10 【问题描述】:

我正在为课程的一些代码编写解释,并且不小心交替使用了 methodfunction 这两个词。我决定回去修正措辞,但在我的理解中遇到了一个漏洞。

据我了解,如果子例程不作用于类的实例(其效果仅限于其显式输入/输出),则子例程是 function,如果它在一个类的实例(它可能会对实例产生副作用,使其不纯)。

有一个关于该主题的good discussion here。请注意,根据公认答案的定义,静态 method 实际上应该是一个函数,因为实例永远不会被隐式传递,并且它无权访问任何实例的成员。

考虑到这一点,静态methods 不应该是函数吗?

根据他们的定义,它们不会作用于类的特定实例;他们只是因为关系而与班级“绑在一起”。我已经看到一些好看的网站将静态子例程称为“方法”(Oracle、Fredosaurus、ProgrammingSimplified),所以要么他们都忽略了术语,要么我遗漏了一些东西(我的猜测是后者)。

我想确保我使用了正确的措辞。有人可以澄清一下吗?

【问题讨论】:

我一直以为是php中的函数和Java中的方法。基本上是同一个东西,名字不同 理论计算机科学和语言如何应用它是有区别的。 JLS 没有区别,称其为方法。 ***.com/a/155655/2669716 看一下 Python 中“函数”和“方法”的定义可能会很有趣,其中有 区别:基本上,函数是带有符号表和调用约定的代码,而方法是将函数放入类中时得到的。但是,即使对于了解 Python 的人来说,差异也很微妙。 我学理论的时候,学过函数有返回值,而过程没有。然后我学习了java调用函数和过程方​​法。现在我正在尝试函数式编程并且函数是幂等的。这些术语会根据上下文改变含义。 【参考方案1】:

8.4.3.2 的这句话可能会有所帮助:

声明为static 的方法称为类方法

未声明static 的方法称为实例方法 [...]。

类方法:与类关联。 实例方法:与实例相关联。

Java 只是希望您“思考面向对象”。此外,静态方法可以访问可能包括状态的周围范围。在某种程度上,类就像一个对象本身。

【讨论】:

话虽如此,虽然“函数”作为 Java 中的执行单元在技术上是正确的,但几乎所有 Java 中首选的命名法是“方法”,因为所有 Java 函数都是类的一部分或接口(不包括 lambda,也许还有一些我不知道的其他事情)。 Lambda 实际上是匿名内部类,带有 @FunctionalInterface 注释和 1 个方法。 lambda 只是语法糖,在这方面没有什么新东西。 @AdamArold Lambdas 比匿名内部类要好得多。例如,非捕获 lambda 可以跨特定表达式的多个求值共享一个实例。 (但你是对的,它们最终会被编译为静态和实例方法。) @Radiodef 也许更好的表达方式是“所有 lambda 表达式都可以替换为等效的非 lambda 表达式,而不需要对包含 lambda 表达式的文件以外的文件进行任何更改”或其他内容像这样。 我很尴尬。我来自 Scala,但仍然设法错过了类本身类似于对象的事实。谢谢。【参考方案2】:

简单的答案是,当 Java 决定将一切都称为“方法”时,他们并不关心理论计算机科学中函数和方法之间的区别。

【讨论】:

没错。在 Java 7 之前(包括 Java 7),您甚至不会在 Language Specification 中找到“函数”这个词 尽管我很喜欢这个答案的简单性,但我认为 Radiodef 的答案更符合预期,因为它提到了类本身作为对象的关键点。不过谢谢。 有趣的是,这与早期语言不区分函数和子例程的决定并行。 我很惊讶这个答案得到了如此多的支持。首先,这个答案假装类方法不存在。其次,这几乎不是 Java 中引入的概念。例如,Smalltalk 中已经存在类方法,在 Java 出现之前它已经存在了几十年。 @Malcolm 我必须同意你的看法。在考虑了其他答案之后,这似乎是错误的。对 Java 的创建者来说,这并不是冷漠,除非他们真的不在乎,但最终还是正确地命名了它。【参考方案3】:

静态方法不完全是函数,区别很细微,但很重要。

仅使用给定输入参数的静态方法本质上是一个函数。

但是静态方法可以访问静态变量和其他静态函数(也使用静态变量),所以静态方法可能有一个 state,这与定义为 stateless。 (附录:虽然程序员通常对使用“函数”作为定义并不那么严格,但计算机科学中的严格函数只能访问输入参数)。所以定义这种访问静态字段的情况,说静态方法总是函数是无效的。

证明使用“静态方法”的另一个区别是您可以在 C 中定义可在任何地方访问的全局函数和全局变量。如果您无法访问包含静态方法的类,则这些方法也无法访问。因此,与全局函数相比,“静态方法”的范围受到设计的限制。

【讨论】:

我有点喜欢这个答案,但想更好地理解一些事情。这不是更“纯”与“副作用”的功能,而不是功能与方法吗?还是因为副作用而导致这种方法?我只是在这里集思广益。 这个答案是对的。但是,有人可能会争辩说,许多(大多数?)语言中的函数可以访问全局变量,因此它们通常不是严格无状态的(相同的输入,相同的输出)。在 Java 静态方法的情况下,访问类变量可以被认为等同于访问“全局”(即不是函数/方法的本地)变量——类实例是一种命名空间。 @leonbloy Pure Functional programming languages 像 Haskell 一样是完全无状态的;没有什么可以称为全局变量。【参考方案4】:

在 Java 中,用户定义的类实际上是 java.lang.Class 的子类的实例。

从这个意义上说,静态方法附加到概念类的实例:它们附加到 java.lang.Class 的子类的实例。

考虑到这一点,术语“类方法”(Java 静态方法的替代名称)开始变得有意义。并且“类方法”一词可以在很多地方找到:Objective C、Smalltalk 和 JLS——仅举几例。

【讨论】:

这个子类可以有两个实例吗? 当然,你可以在不同的类加载器中加载一个类(得到ClassCastExceptions并显示消息“cannot cast CustomClass to CustomClass”的原因)。 @Random832——有点像。您可以在同一个 JVM 中拥有同一个 Class 子类的两个(或多个)实例,只要每个实例都有自己独立的类加载器。每个类加载器不能多次实例化同一个 Class 子类。它有点令人困惑,与经典 OO 概念的类比在这一点上开始变得有点牵强。 @MikeClark 如果我这样做,他们真的一样吗?就像,即使类本身是它的不同实例,Class 子类也会是同一个类吗?类加载器让我很困惑。我可以通过传入对它的引用,从同一类的另一个类加载器的实例调用(没有反射)一个类加载器的类实例的静态方法吗?如果他们有不同的方法呢? @Random832 “有点?”从纯粹的 OO 理论的角度来看,一个类的任意两个实例是否真的完全相同?至少同一类的两个在其他方面相同的实例将具有不同的地址。否则,我们怎么可能有两个东西呢?唯一与某物完全相同的,就是它本身。【参考方案5】:

在计算机科学中,函数清楚地映射到静态方法。但是一个类的“方法”有点泛型,比如“成员”(字段成员,方法成员)。有这样的措辞

数据成员和方法成员有两个独立的名称空间:.x 和 .x() 可以共存。

所以原因是,正如哲学家路德维希·维特根斯坦所说,语言是一种具有不同语境的工具。在上面的引用中,“方法”是一个很好的绰号,用于对“成员”进行分类。

【讨论】:

【参考方案6】:

你的想法是对的,而且是有道理的。它只是在 Java 社区中没有建立的术语。让我解释一些有助于理解术语存在的原因。

Java 是一种基于类的面向对象语言。方法始终是类或实例的成员(这也是对其他编程语言有效的一般语句)。我们认为类和实例都是对象。

实例方法(动态)

你不能直接从一个类中调用这个方法,你必须创建一个实例。每个实例都引用该方法。您可以使用完全相同的方法签名(子类化时)覆盖方法定义,即引用指向不同的方法(具有相同的签名,但可以具有不同的方法体)。该方法是动态的。

类方法(静态)

您只能从类中直接调用此方法,即您不需要创建该类的实例。在整个程序中,该方法只有一个全局定义。当方法被声明为静态时,您不能覆盖完全相同的方法签名,因为只有一个定义对整个程序有效。请注意,该方法是类对象本身的成员,实例对该方法具有所有相同的唯一(和固定)引用。

【讨论】:

【参考方案7】:

这里是另一个术语,使用Scala 作为助记符: 在 Scala 中,您有 objects,它们是隐式定义的类 1 的单例实例。

根据您的定义,我们可以调用属于 object 的这些子例程方法,因为它们在类的单个实例上运行。 此外,对象还将定义类 A,并将对象 A 中的所有方法创建为类 A 上的静态方法(用于与 Java 交互) [2]。

因此,我们可以说 Java 类 A 的静态方法访问与 Scala 单例实例相同的成员,根据您的定义,它们应该被称为类 A 的(静态)方法

【讨论】:

很好的比较。我知道 Scala,所以您的 object 参考很有意义。谢谢。【参考方案8】:

当然,主要区别在于——方法可以使用静态字段,而不仅仅是方法参数。 但还有一个额外的——多态性! 评估类 A.doTheSameStaticMethod() 和 ClassB.doTheSameStaticMehod() 的结果将取决于类。在这种情况下,函数是无能为力的。

【讨论】:

【参考方案9】:

每个类都有一个对象来表示它,它是Class 类的子类的一个实例。静态方法实际上是这些对象上的实例方法,这些对象是 Class 的子类的实例。它们可以以静态字段的形式访问状态,因此它们不仅限于(无状态)函数。它们是方法。

【讨论】:

以上是关于为啥将静态方法视为方法?的主要内容,如果未能解决你的问题,请参考以下文章

将指针传递给静态方法

为啥pycharm建议将方法更改为静态

java调用同一个类中的方法为啥要将方法申明成静态?

powermock mock静态方法为啥会执行

为啥主要方法应该是静态的[重复]

为啥每次调用 Spring MVC 服务中的简单方法都比静态方法慢?