为什么Java有`void`方法?


52

/为什么Java需要有void方法?参考

任何声明为void的方法都不会返回值。

就我所能想到的,void通过返回状态标志,被调用的对象或,将更好地服务于的每次使用null

这将使每个调用成为可分配的语句,并有助于构建器模式和方法链接。仅出于效果而调用的方法通常会返回布尔值或泛型Success类型,或者在失败时引发异常。


这样我们就不需要特殊的语法来编写子例程。我们可以使用同一功能使用。
candied_orange

Answers:


159

因为“此功能可以成功或失败并且具有足够的自我意识以至于可以分辨出差异”与“没有有关此功能效果的反馈”之间存在差异。没有void,您将无休止地检查成功代码,并认为自己正在编写强大的软件,而实际上却什么也不做。


10
但是,它是成功还是失败是通过抛出或不抛出异常而不返回标志来表达的。而且返回void并没有说明方法或函数是否具有足够的自我意识,可以适当地抛出。
Volker Siegel

12
@VolkerSiegel:答案的上下文是问题。OP建议在所有使用void的情况下,该函数都应返回一个状态标志,指示失败的成功。在那种情况下,返回void确实表示该函数实际上没有任何返回值。强制此类函数始终返回0表示成功将使该函数的用户误以为他们正在进行错误检查,而实际上无论成功与否,返回值始终为0。
slebetman '17

19
@VolkerSiegel在很多情况下,异常不是正确的处理方式。实际上,我想说的是,对的是正确的比错的更为罕见-例外意味着发生了一件可怕的事情,需要加以考虑。预期的失败案例永远不要使用异常。
·塞尚

35
@GabeSechan不,如果该方法无法履行其合同,则应引发异常。如果该方法需要一个非空引用,但它得到一个,那是预期的失败,应该抛出。
安迪

5
有许多替代语法可以解决此问题。他们选择此(笨拙)解决方案的原因是因为C使用了它。
Marco van de Voort

96
  1. 因为C具有void类型,所以Java的设计遵循了C语言家族的许多约定。
  2. 有许多您不希望返回值的函数。Success无论如何,您将如何处理“泛型类型”?实际上,表示成功的返回值在Java中的重要性甚至不如C中重要,因为Java具有表示失败的异常,而C却没有。

8
>“无论如何,您将如何处理“通用成功类型”?—在编写通用代码时,单个值类型(称为单元类型btw)非常有用,因为它消除了“仅返回”但不特别返回任何函数的特殊情况。但是,当Java最初被创建时,它具有更少的表现型类型系统(没有类型参数),因此并没有太大的实际差异。现在改变为时已晚。更多现代语言实际上避开了无效的“类型”(实际上,它甚至不是类型,只是方法的特殊标记),而改用单元类型。
Sarge Borsch,

9
@SargeBorsch Java的Void类型充当单元类型(例如,对于泛型),尽管它与void(不同的是:每次您发明新的类型来修复在先前版本中弄乱的类型时,可爱的小猫都会死亡)不同。如果有人不熟悉单位类型,这是一个不错的介绍:en.wikipedia.org/wiki/Unit_type
Ogre Psalm33 '17

1
@ OgrePsalm33并没有真正用作单元类型(至少以实际的方式–仍然必须编写“ return null;”以从具有Void返回类型的方法中返回,并且AFAIK编译器确实在堆栈上为Void引用分配了空间,而不是利用读取这些值毫无意义的事实),这只是在“适当的”单元类型更合适的地方使用它的约定。
Sarge Borsch

3
@immibis,因为根据定义单位类型必须具有一个唯一的值,而Void没有值。是的,可以使用null(实际上这是唯一的选择),但是null是特殊的事情,Java中引用类型的任何值都可能为null(这是语言设计中的另一种错误)。而且,正如我之前说的,如果您使用Void返回类型而不是voidmark,则Java编译器不是您的朋友。
Sarge Borsch

5
@SargeBorsch Void只有一个值,即nullnull是大多数类型的成员这一事实与Void单元类型是否相关。
user253751 '17

65

语言具有类型的最初原因void是因为像in中一样C,语言的创建者不想像Pascal那样使过程函数的语言语法不必要地复杂化。

那是最初的原因。

你的建议:

返回状态标志

那是不可以的。我们不使用状态标志。如果出现问题,我们会通过异常报告。

被调用的对象

流利的调用样式是最近的发展。今天,许多非常高兴地使用流利的程序员,如果不得不使用不支持该接口的接口,他们甚至会在创建Java时出生。

返回null

这将迫使您将一个方法声明为返回某种东西,而实际上它并未返回任何东西,因此对于正在查看接口以试图弄清楚它做什么的人来说,这将非常令人困惑。人们不可避免地会发明一些别无用处的类,表示“无返回值”,以便所有没有任何要返回的函数都可以返回对该类的空引用。那将是笨拙的。幸运的是,有一个解决方案,称为void。这就是您问题的答案。


3
请提供“原始原因...”的来源。据我所知,真正的原因是因为C被设计为可移植的汇编程序。
托尔比约恩Ravn的安徒生

6
@ThorbjørnRavnAndersen实际上,您是不是真的要求我提供Java语法是从C语法派生的说法的来源?
Mike Nakis

3
@ThorbjørnRavnAndersen哦,我明白了,我误会了。因此,您需要我关于C而不是Java的主张的资料。好的,对不起,我没有任何消息来源。但我认为这很明显。(我以前在1987年在Pascal编程,当时我切换到C。我的天哪,那是30年前了。)
Mike Nakis

6
这不是显而易见的。C大概是在Pascal的同时开发的,他们很可能甚至都不知道它的存在。请不要将假设陈述为事实。
托尔比约恩Ravn的安徒生

12
真正原始原因是,默认为C函数返回int,因此关键字加入之后K&R,以指示“无返回类型”。
user207421 '17

20

有些方法(例如)System.out.println不会返回任何有用的信息,而纯粹是出于这种副作用而调用的。void对编译器和代码阅读器来说是一个有用的指示,指示没有返回有用的值。

返回null而不是void意味着您将有NullPointerException片刻时间将此值用于任何事物。因此,您将编译时错误换成运行时错误,这更糟。此外,您必须将返回类型定义为Object,这会产生误导和混乱。(并且链接仍然不起作用。)

状态码通常用于指示错误情况,但是Java为此有例外。

this无法从静态方法返回。

返回通用Success对象将没有任何用处。


1
完全同意您的观点,这是最简单明了的答案。
webo80

11

原因之一是,返回除以外的任何内容都可能会产生误导null。例:

应该Arrays.sort(a)返回什么?

如果您认为a应该返回该值以便可以将调用链接起来(这似乎是从您的问题来看,您的回答),则不再清楚返回值是原始对象的副本还是原始对象本身。两者都有可能。是的,您可以将其放入文档中,但是它很含糊,以至于您不应该首先创建歧义。

另一方面,如果您认为null应该返回该值,那就是以下问题:返回值可能会为调用者提供哪些信息,以及为什么在不return null传递任何信息时必须强制程序员进行编写。

而且,如果您返回的其他内容完全是荒谬的(例如的长度a),那么使用该返回值确实会使您感到迷惑–只需考虑说int len = Arrays.sort(a)而不是int len = A.length


1
公平地说,并不一定要求程序员编写代码return null;-JVM也可以强制要求,如果控制权通过除显式returnthrow语句之外的其他方法终止于函数的末尾,则将null其隐式返回给调用方。IIRC C会执行此操作,但在这种情况下,返回值是未定义的(这将是当时用于返回值的位置中的任何值)。
CVn

2
您的粗体问题的正确答案是“排序数组”。
Bryan Boettcher

2
@BryanBoettcher:您没有阅读其余内容吗?
Mehrdad

1
@MichaelKjörling:这是Java语言的基本属性,可以防止此类错误,即,如果您忘记return声明,则不会出现“未定义的行为” 。而是,您得到一个编译器错误,告诉您您的错误。插入一个隐式return null;方法比“未定义的行为”要好,但是不多。仅当返回类型没有意义时这才有用,但是当编译器从何处得出结论时,返回值没有意义void呢?
Holger

2
@MichaelKjörling:“如果return真正不添加任何值……”,正如前面所说,编译器没有指示符,如果没有void类型,则返回将不添加任何值。实际上是相反的,第一个假设是声明一个返回类型的方法想要return该类型有用的东西。而且我们还没有讨论没有null值的原始类型,因此,编译器注入的任何默认值都会与潜在有用的返回值范围冲突。
Holger

7

返回boolean始终等于true您建议的a不仅毫无意义(返回值不包含任何信息),而且实际上会产生误导。在大多数情况下,最好使用返回类型,其中仅携带的实际可用尽可能多的信息-这就是为什么在Java中,你必须booleantrue/ false,而不是返回一个int4个十亿可能的值。同样,boolean在只有一个可能值的情况下返回具有两个可能值的“ a”(“一般​​成功”)会产生误导。

这也将增加不必要的性能开销。

如果仅将方法用于其副作用,则没有任何要返回的内容,并且返回类型void恰好反映了这一点。潜在的罕见错误可以通过异常发出信号。

可以改进的一件事是制作void一个实际的类型,例如Scala的Unit。这将解决一些问题,例如处理泛型。


有趣的事实:List.add确实会返回true,但这是为了与的更广泛的合同兼容Collection.add,因此Set.add可能会返回truefalse
Holger

4

Java是一种相对古老的语言。在1995年(当它创建时),此后不久,程序员非常担心进程控制处理器的时间以及内存的消耗。返回void将消除函数调用的几个时钟周期和一些内存消耗,因为您不必将返回值放在堆栈上,并且随后也不必删除它。

高效的代码不会给您带来您永远不会使用的东西,因此将void放入具有无意义的返回值的东西中要比返回成功值要好得多。


14
现在,那些非常关心速度的程序员无论如何都不会使用Java,因为Java的第一个实现非常慢。太慢了,以至于许多当前不使用它的人仍然对Java感到厌烦和慢速的代名词印象深刻。
Sarge Borsch

8
@SargeBorsch让我想起20年前的一个古老编程笑话。“敲,敲。” “谁在那儿?” …………很长的停顿…………“ Java”
Dan Neely

4
@DanNeely和一段时间之后,一辆AI驱动的汽车全速撞墙,甚至没有试图使用刹车。它是用Java编写的。因此,Java 现在不会刹车!(这是俄语中的双关语,无法很好地将其翻译成英文)
Sarge Borsch

2
@SargeBorsch您所拥有的就像一个双关语,即使它在翻译中失去了细微差别。翻译双关语可能比诗歌难。
Dan Neely

3
优秀的程序员仍然关心内存的性能和消耗,这甚至在今天仍然不必要地要求人们返回垃圾。
Sklivvz

2

我已经读过一些论点,建议第二种选择(return this)应该是方法而不是void。这是一个很好的主意,但是在创建Java时,生成器风格的方法并不流行。如果它当时和现在一样流行,那可能就是采取的方法。返回null是一个非常糟糕的主意IMO。我希望null根本不用语言。

现在的问题是,如果方法返回其自身类型的实例,则不清楚该对象是新对象还是相同对象。通常,它并不重要(或不应该),但其他时候确实很重要。我想可以将语言更改为隐式返回thisvoid方法。我目前唯一能想到的问题是,如果以后更改方法以返回相同类型的新对象,则不会有编译警告,但这可能没什么大不了的。当前的类型系统现在不在乎这些类型的更改。它如何与继承/接口交互需要一些考虑,但是它将允许没有构建器样式的旧API像它们一样容易被调用。


2
@Cody好一点,这不适用于静态方法。
JimmyJames

14
@ marisbest2哪个参数?
8bittree

3
好吧,如果null不是语言的一部分,人们将使用各种各样的哨兵值,这意味着“请忽略此参数/返回值,它不包含任何明智的值”。问题null不在于事物本身,而在于它往往被错误地使用。我敢打赌,只有null用大量的自定义解决方案替换标准化后,这个问题才会被夸大,在每种解决方案中,每个实现的行为都将像静默忽略用法一样,或者抛出特殊的异常而不是标准的空指针异常,等等
cmaster

2
@cmaster的明显替代null是适当的可选类型(例如,Maybe在Haskell中)。Java中空值的问题在于,没有一种明智的方法可以立即确定一个值在实践中是否可以为空。这会导致以下两种情况:不必要的防御性编程(在无意义的地方检查空值,从而增加代码中便便的百分比),以及生产中意外的NPE(如果忘记检查需要在哪里)。是的,它可以通过注释部分解决,但它们是可选的,并非始终处于选中状态等。因此,它们不会在第三方代码的边界上为您省钱。
Sarge Borsch

2
@SargeBorsch我同意:-)我的反对意见是反对没有标准方式表达“请忽略此值”的语言。null为此可以很好地工作(很好=简单),但是具有可靠语义的标准化可选值绝对是另一个很好的选择(很好=安全)。
cmaster

2

另一个方面:

Java是具有相当严格功能的静态语言。这意味着严格确定了许多其他语言(c / f Ruby,Lisp等)中相当开放或动态的内容。

这是一般的设计决策。“为什么”很难回答(好吧,因为语言的设计者认为这会很好!)。“针对”的含义很明确:它使编译器能够检测到很多错误,这对于任何一种语言来说通常都是相当不错的功能。其次,它使语言推理相对容易。例如,用Java语言(的子集)创建形式正确性证明相对容易;相比之下,在像Ruby等人的动态语言中这几乎是不可能的。

这种想法渗透到了语言中,例如,方法可能抛出的可能异常的强制声明,interfacevs 。的单独类型,class以避免模棱两可的多重继承,等等。就其本质而言(一种静态命令式OOP现实语言,重点是编译时错误处理),这些东西实际上是相当优雅和强大的。它们比理论(科学)语言更接近,而理论(科学)语言是专门为探索这些问题而设计的,比以前的任何其他现实世界语言(当时请注意)。

所以。具有严格的void类型是一条明确的消息:该方法在句点期间不返回任何内容。就是这样。通过强制执行替换它总是返回某些东西将导致更多的动态行为(例如在Ruby中,每个 def总是具有显式或隐式的返回值),这对于可证明性和推理是不利的;或通过使用此处的其他机制将其膨胀。

(和NB,红宝石(例如)处理此不同,它的解决方案仍然是完全一样接受 Java的,因为它有一个完全不同的理念。例如,它完全抛出可证性与合理性窗外,同时把一重点语言的极高表达能力。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.