我正在为一门课程的一些代码编写一个解释,无意中使用了"EDOCX1"(0)和"EDOCX1"(1)两个可互换的词。我决定回去修改措辞,但在我的理解上遇到了一个漏洞。
据我所知,如果子程序不作用于某个类的实例(其效果仅限于其显式输入/输出),则它是一个function;如果子程序作用于某个类的实例,则它是一个method(它可能会对使其不纯的实例产生副作用)。
关于这个话题,这里有一个很好的讨论。注意,根据接受的答案的定义,静态method实际上应该是一个函数,因为一个实例从未被隐式传递,并且它无权访问任何实例的成员。
不过,有了这一点,静态methods不应该是函数吗?
根据它们的定义,它们不会对某个类的特定实例执行操作;因为关系,它们只与该类"绑定"。我见过一些好看的站点,它们将静态子例程称为"方法"(Oracle、Fredosaurus、ProgrammingSimplified),所以要么它们都忽略了术语,要么我遗漏了一些东西(我猜是后者)。
我想确定我用的是正确的措辞。有人能清理一下吗?
- 我一直认为它是PHP和Java中的方法的函数。基本上相同,名字不同
- 理论上的计算机科学和语言如何应用有区别。JLS没有区别,称之为方法。
- stackoverflow.com/a/155655/2669716
- @Jeroenvanevel是正确的。术语:类的实例是一个对象,它有实例方法。类可以有静态方法,并且不会应用于从类创建的任何实例。
- 在python中查看"函数"和"方法"的定义可能很有意思,这两者之间有一个区别:基本上,函数是一块带有符号表和调用约定的代码,而方法是将函数放入类中时得到的。尽管如此,这种差异还是相当微妙的,即使对于了解Python的人也是如此。
- 我将借此机会解释当编程语言的作者开始使用术语时,它是多么令人困惑,因为静态方法与函数完全相同。
- 当我学习理论的时候,我学到了函数返回一个值,而过程不返回。然后我学习了Java调用函数和过程的方法。现在我正在尝试函数编程,函数是等幂的。这些术语根据上下文改变了含义。
- 在大多数OO语言中,静态方法和函数的区别在于允许方法访问类的私有变量和方法。
- @radiodef,为什么这不是链接问题的副本?stackoverflow.com/questions/155609/…
- @哈比,我倾向于把它转过来,问它是如何复制的。这个问题引用了这个问题中答案的定义,并询问(为什么这么多)Java为什么不遵循它们。我不清楚参考问答如何解决这一问题。
- 术语是特定于语言的。Java有很多术语,它们可能在其他语言的上下文中使用或不使用。没有通用术语。它就像一种语言的语法。
8.4.3.2中的引用可能有助于:
A method that is declared static is called a class method.
A method that is not declared static is called an instance method [...].
Java只是想让你"思考面向对象"。另外,静态方法可以访问可能包含状态的周围范围。在某种程度上,类就像一个对象本身。
- 这就是说,虽然"函数"在Java中是一个执行单元,但在几乎所有的Java中,首选的命名是"方法",因为所有Java函数都是一个类或接口的一部分(不包括lambdas,可能还有一些我不知道的东西)。
- lambda实际上是匿名的内部类,带有@FunctionalInterface注释,在hood下有1个方法。lambda只是句法上的糖分,在这方面没有什么新的。
- @阿达马罗德·兰伯斯比一个匿名的内部阶级要花哨得多。例如,非捕获lambda可以跨特定表达式的多个计算共享一个实例。(但您是对的,它们最终被编译为静态方法和实例方法。)
- @radiodef可能是更好的表达方式,"所有lambda表达式都可以替换为等效的非lambda表达式,而不需要对包含lambda表达式的文件以外的文件进行任何更改"或类似的内容。
- 我很尴尬。我来自scala,仍然设法忽略了类本身是类似对象的事实。谢谢您。
- 另外,IIRC,一个类方法,如果作为参数传递一个类的实例,就可以访问私有成员。(以及任何静态私有成员。)
简单的答案是,当Java决定将所有事物称为"方法"时,他们并不关心理论计算机科学中函数和方法的区别。
- 我很喜欢这个答案。天才知道哪里可以忽略事物。
- 这是正确的。直到和包括Java 7在内,在语言规范中你甚至找不到"函数"一词。
- 尽管我很喜欢这个简单的答案,但我认为radiodef的答案更符合实际,因为它提到了类本身作为对象的关键点。不过谢谢你。
- 有趣的是,早期语言决定不区分函数和子例程,这与此类似。
- 这个答案得到如此多的赞成票,真让我感到不快。首先,这个答案假装类方法不存在。其次,这并不是Java中引入的概念。例如,SimultTalk中已经存在类方法,这在Java成为一件事情之前已经存在了几十年。
- @马尔科姆,我必须同意你的看法。在考虑了其他答案之后,这似乎是错误的。对Java创建者来说,这不是冷漠,除非他们真的不在乎,但最终正确地命名它。
- 用"类方法"@马尔科姆,你是指Java的静态方法吗?BitcoinM的观点是,在Java语言中,我们不使用"函数"这个词,也不使用静态方法。这也隐含在radiodef的答案中。我们可以谈论OOP理论,但是程序员不区分,Java语言规范也没有。
- @格雷再次引用了JLS的话:"一个声明为静态的方法叫做类方法。"老实说,我不知道如何将你的评论与答案的实际内容联系起来。我看到了一个完全不同的观点,就像Java创建者发明了一些错误的术语,实际上它是完全正确的,(b)在Java之前就已经存在了。至于区别,我很清楚,如果Java中的所有函数都是方法,那么调用它们的方法就更有意义了。
静态方法不完全是函数,区别很细微,但很重要。
仅使用给定输入参数的静态方法本质上是一个函数。
但是静态方法可以访问静态变量和其他静态函数(也可以使用静态变量),因此静态方法可能具有一种与定义为无状态的函数根本不同的状态。(附录:虽然程序员通常不那么严格地使用"函数"作为定义,但计算机科学中严格的函数只能访问输入参数)。因此,定义访问静态字段的这种情况,说静态方法总是函数是无效的。
另一个解释"静态方法"用法的不同点是,您可以在C中定义派生的全局函数和全局变量可以在任何地方访问。如果您不能访问包含静态方法的类,那么这些方法也是不可访问的。因此,与全局函数相比,设计限制了"静态方法"的范围。
- 我有点喜欢这个答案,但想更好地理解一些事情。这难道不是一个"纯粹的"对"副作用的"函数,而不是函数对方法吗?或者是因为副作用,一种方法是这样的?我只是在头脑风暴。
- 这个答案是对的。然而,有人可能会说,功能在许多(大多数?)语言可以访问全局变量,因此它们通常不是完全无状态的(相同的输入、相同的输出)。在Java静态方法的情况下,访问类变量可以被视为等同于访问"全局"(即,不是本地到函数/方法)变量——类实例是命名空间的类型。
- @像haskell这样的leonbloy纯函数编程语言是完全无状态的;没有什么可以称为全局变量的。
在Java中,用户定义的类实际上是JavaLang.C类的子类的实例。
从这个意义上讲,静态方法被附加到概念类的一个实例上:它们被附加到java.lang.class子类的一个实例上。
考虑到这一点,术语"类方法"(Java静态方法的另一个名称)开始有意义。术语"类方法"可以在许多地方找到:目标C、Smalltalk和JLS——仅举几个例子。
- 这个子类可以有两个实例吗?
- 当然,您可以在不同的类加载器中加载一个类(获取带有消息"cannot cast customclass to customclass"的ClassCastExceptions的原因)。
- @随机832——差不多。您可以在同一个JVM中拥有两个(或更多)同一类子类的实例,只要每个实例都有自己的独立类加载器。不能为每个类加载器多次实例化同一类子类。它变得有点混乱,对经典OO概念的类比在这一点上开始变得有点模糊。
- @如果我这样做,他们真的是一样的吗?比如,即使类本身是它的不同实例,类子类是否也是同一个类?对我来说,类装入器是相当混乱的。我可以从同一类的另一个类加载器实例调用(不进行反射)某个类的一个类加载器实例的静态方法,方法是通过传递对该实例的引用?如果他们有不同的方法呢?
- @Random832"有点?"从纯OO理论的观点来看,类的任何两个实例是否真的完全相同?同一类中至少有两个相同的实例具有不同的地址。否则,我们怎么能有两样东西呢?唯一与某物完全相同的东西,就是它本身。
- 所谓"真正相同",是指类的类(即类的子类本身)是==相等的,能够将其中一个传递给class类型的参数,等等。
- 从这个意义上说,它们肯定是不一样的。JVM不允许您将com.foo.bar(从Classloader A)强制转换为com.foo.bar(从Classloader B),并且它们不会被视为==相等。回复:普通医学:我不太确定。一般检查是在编译时完成的,所以我不确定在运行时之前您是否会发现问题。
- 我正在讨论的是[a]com.foo.bar.class的类是否与[b]com.foo.bar.class的类相同,也就是说,它们是否都是一个假设的"ClassComFoobar扩展类"的单独实例,或者每个加载程序的com.foo.bar.class的实例是否也有两个单独的ClassComFoobar。也就是说,com.foo.bar.class.getClass()会不会返回a)每个加载程序两个独立的值b)一个仅由com.foo.bar类共享的值或c)java.lang.class.class?
- @random832[a]com.foo.bar.class和[b]com.foo.bar.class不是JVM中某个单例"ClassComfoobar扩展类"的实例。将分别有[A]"ClassComfoobar扩展类"和[B]"ClassComfoobar扩展类"。这两个类可以包含99%不同的字节码(它们可能只有一个共同的名称)。另一方面,这两个类可以(并且经常这样做)有相同的字节码,从同一个源代码加载。在这种情况下,[a]条和[b]条在概念上是相同的,但仍然被JVM视为完全无关的。
- 那么"是否可以有这个子类的两个实例?"是"不"。
- 我同意你的看法,在一定的(非常真实的)背景下,答案肯定是"不"。我感兴趣的两个上下文是:概念类(大致上,在UML级别)和"物理"类(大致上,在JVM级别)。对我来说,如果[A]条和[B]条具有从同一资源加载的相同字节码,那么它们在概念上是同一类的两个实例。JVM从物理上分离了[A]条和[B]条,并将它们视为不相关的,但这并不意味着它们在所有可能的方法中都是不相关的(甚至是不可信的),以考虑系统的结构。
在计算机科学中,函数清楚地映射到静态方法。但是类的"method"有点通用,比如"member"(字段成员、方法成员)。有这样的字眼
Data members and method members have two separate name spaces: .x and .x() can coexist.
因此,原因是,正如哲学路德维希·维特根斯坦所说,语言是一种具有不同背景的工具。"方法"是上面引文中分类"成员"的好名字。
你的想法是正确的,而且是有道理的。它只是Java社区中没有建立的术语。让我解释一些内部机制,可以帮助理解术语存在的原因。
Java是一种基于类的面向对象语言。方法始终是类或实例的成员(这也是对其他编程语言有效的常规语句)。我们认为类和实例都是对象。
实例方法(动态)
不能直接从类调用此方法,必须创建实例。每个实例引用该方法。您可以使用完全相同的方法签名(子类化时)覆盖方法定义,即引用指向不同的方法(具有相同的签名,但可以具有不同的方法体)。方法是动态的。
类方法(静态)
只能直接从类中调用此方法,即不需要创建该类的实例。在整个程序中,该方法只有一个全局定义。当方法声明为静态时,不能覆盖完全相同的方法签名,因为只有一个定义对整个程序有效。请注意,该方法是类对象本身的成员,实例对该方法具有所有相同的唯一(和fix)引用。
下面是对术语的另一种理解,使用scala作为助记键:在scala中有objects,它是隐式定义的类1的单例实例。
根据您的定义,我们可以调用这些属于object方法的子例程,因为它们在类的单个实例上操作。此外,对象还将定义类A,并将对象A中的所有方法创建为类A上的静态方法(用于与Java接口)[2 ]。
因此,我们可以说Java类的静态方法访问与Scala SuntLon实例相同的成员,根据您的定义,它应该被称为类A的(静态)方法。
- 比较好。我知道斯卡拉,所以你提到的object很有意义。谢谢您。
当然,主要的区别是-方法可以使用静态字段,而不仅仅是方法参数。但还有另外一个-多态性!评估类A.DothesameStaticMethod()和类B.DothesameStaticMethod()的结果将取决于类。在这种情况下,功能是不起作用的。
每个类都有一个对象来表示它,它是Class类的一个子类的实例。静态方法实际上是这些对象上的实例方法,它们是类的子类的实例。它们以静态字段的形式访问状态,因此它们不局限于(无状态)函数。它们是方法。