Warum hat Java "void" -Methoden?


51

Muss / warum Java voidMethoden haben? Hinweis :

Jede als ungültig deklarierte Methode gibt keinen Wert zurück.

Soweit ich denken kann, ist es für jede Verwendung von voidbesser, ein Statusflag zurückzugeben, das Objekt aufzurufen oder null.

Dies würde jeden Aufruf zu einer zuweisbaren Anweisung machen und die Erstellung von Mustern und die Verkettung von Methoden erleichtern. Methoden, die nur für ihre Auswirkungen aufgerufen werden, geben normalerweise einen Booleschen oder einen generischen SuccessTyp zurück oder lösen bei Fehlern eine Ausnahme aus.


Damit wir zum Schreiben von Unterprogrammen keine spezielle Syntax benötigen. Wir können die gleichen Funktionen verwenden.
candied_orange

Antworten:


158

Denn es gibt einen Unterschied zwischen "Diese Funktion kann erfolgreich sein oder scheitern und ist sich der Tatsache bewusst, dass sie den Unterschied erkennen kann" und "Es gibt kein Feedback zur Auswirkung dieser Funktion." Ohne würden voidSie endlos die Erfolgscodes überprüfen und glauben, dass Sie robuste Software schreiben, obwohl Sie nichts dergleichen tun.


10
Ob dies jedoch erfolgreich war oder nicht, lässt sich durch das Auslösen oder Nichtauslösen einer Ausnahme und nicht durch Zurückgeben eines Flags ausdrücken. Und das Zurückgeben voidsagt nichts darüber aus, ob die Methode oder Funktion selbstbewusst genug ist, um entsprechend zu werfen.
Volker Siegel

12
@ VolkerSiegel: Der Kontext der Antwort ist die Frage. Das OP schlug vor, dass in allen Fällen, in denen void verwendet wird, die Funktion stattdessen ein Statusflag zurückgeben sollte, das den Erfolg eines Fehlers anzeigt. In diesem Zusammenhang besagt returning void tatsächlich, dass die Funktion buchstäblich nichts zurückzugeben hat. Wenn Sie eine solche Funktion zwingen, immer 0 zurückzugeben, um den Erfolg anzuzeigen, können Benutzer der Funktion falsch sicher sein, dass sie eine Fehlerprüfung durchführen, wenn der Rückgabewert in Wirklichkeit immer 0 ist, unabhängig von Erfolg oder Misserfolg.
Slebetman

19
@VolkerSiegel Es gibt eine Vielzahl von Situationen, in denen Ausnahmen nicht die richtige Vorgehensweise sind. Tatsächlich würde ich sagen, dass es seltener ist, dass eine Ausnahme richtig als falsch ist - eine Ausnahme bedeutet, dass etwas Schreckliches passiert ist, das berücksichtigt werden muss. Ein erwarteter Fehlerfall sollte niemals eine Ausnahme verwenden.
Gabe Sechan

35
@GabeSechan Nein, Ausnahmen sollten geworfen werden, wenn die Methode ihren Vertrag nicht erfüllen kann. Wenn für die Methode eine nicht vollständige Referenz erforderlich ist, diese jedoch abgerufen wird, liegt ein erwarteter Fehler vor und sie sollte ausgelöst werden.
Andy

5
Es gibt viele alternative Syntaxen, um das zu beheben. Der Grund, warum sie sich für diese (umständliche) Lösung entschieden haben, ist, dass C sie einsetzt.
Marco van de Voort

95
  1. Weil C einen voidTyp hat und Java so konzipiert wurde, dass es vielen Konventionen der C-Sprachfamilie folgt.
  2. Es gibt viele Funktionen, für die Sie keinen Wert zurückgeben möchten. Was werden Sie mit "einem generischen SuccessTyp" überhaupt machen? Tatsächlich sind Rückgabewerte zur Anzeige des Erfolgs in Java noch weniger wichtig als in C, da Java Ausnahmen zur Anzeige von Fehlern hat und C dies nicht tut.

8
> "Was machen Sie mit" einem generischen Erfolgstyp "überhaupt?" - Wenn Sie generischen Code schreiben, ist ein einzelner Werttyp (er heißt übrigens Unit-Typ) sehr nützlich, da dadurch der Sonderfall von Funktionen beseitigt wird, die "nur zurückgeben", aber nichts Besonderes zurückgeben. Als Java zum ersten Mal erstellt wurde, hat es jedoch ein noch weniger aussagekräftiges Typsystem (ohne Typparameter), sodass es praktisch keine großen Unterschiede gab. Und jetzt ist es zu spät, um sich zu ändern. Moderne Sprachen meiden eigentlich leere "Typ" (eigentlich ist es nicht einmal ein Typ, sondern nur ein spezieller Marker für Methoden) und verwenden stattdessen den Einheitentyp.
Sarge Borsch

9
@SargeBorsch Javas VoidTyp fungiert als Einheitentyp (z. B. für Generika), unterscheidet sich aber leider von void(Randnotiz: Ein Kuscheltier stirbt jedes Mal , wenn Sie einen neuen Typ erfinden, um den Typ zu reparieren, den Sie in der vorherigen Version durcheinander gebracht haben). Wenn jemand nicht mit Einheitentypen vertraut ist, ist dies ein anständiges Intro: en.wikipedia.org/wiki/Unit_type
Ogre Psalm33

1
@ OgrePsalm33 Es handelt sich nicht wirklich um einen Unit-Typ (zumindest in praktischer Hinsicht - man muss immer noch "return null" schreiben, um von einer Methode mit dem Rückgabetyp Void zurückzukehren, und der AFAIK-Compiler reserviert Speicherplatz auf dem Stack für Void-Referenzen (ohne die Tatsache auszunutzen, dass es sinnlos ist, diese Werte jemals zu lesen), ist es lediglich eine Konvention, sie dort zu verwenden, wo ein "richtiger" Einheitentyp angemessener wäre.
Sarge Borsch

3
@immibis, da der Einheitentyp per Definition genau einen Wert haben muss und Void keine Werte hat. Ja, es ist möglich zu verwenden null(in der Tat ist dies die einzige Wahl), aber null ist eine spezielle Sache, jeder Wert des Referenztyps in Java kann null sein (dies ist ein weiterer Fehler im Sprachdesign). Und wie ich bereits sagte, wenn Sie den VoidRückgabetyp anstelle von voidmark verwenden, ist der Java-Compiler nicht Ihr Freund.
Sarge Borsch

5
@SargeBorsch Void hat genau einen Wert, nämlich null. Die Tatsache, dass nulles sich um ein Element der meisten Typen handelt, spielt keine Rolle, ob Voides sich um einen Einheitentyp handelt.
user253751

65

Der ursprüngliche Grund, warum die Sprache einen voidTyp hat, ist C, dass die Ersteller der Sprache die Syntax der Sprache nicht unnötig mit Prozeduren und Funktionen wie Pascal verkomplizieren wollten.

Das war der ursprüngliche Grund.

Deine Vorschläge:

Rückgabe eines Status-Flags

Das ist ein Nein-Nein. Wir verwenden keine Statusflags. Wenn etwas schief geht, melden wir es über Ausnahmen.

das aufgerufene Objekt

Der fließende Stil von Anrufungen ist eine neuere Entwicklung. Viele der Programmierer, die heute sehr gerne fließend arbeiten und die Augen verdrehen, wenn sie jemals eine Schnittstelle verwenden müssen, die dies nicht unterstützt, wurden nicht einmal bei der Erstellung von Java geboren.

null zurückgeben

Das würde Sie zwingen, eine Methode als etwas zurückzugeben, obwohl sie tatsächlich nichts zurückgibt. Es wäre also sehr verwirrend für jemanden, der sich eine Schnittstelle ansieht, um herauszufinden, was sie tut. Die Leute würden unweigerlich eine ansonsten nutzlose Klasse erfinden, die für "kein Rückgabewert" steht, damit alle Funktionen, die nichts zurückzugeben haben, eine Nullreferenz auf eine solche Klasse zurückgeben können. Das wäre klobig. Zum Glück gibt es eine Lösung dafür, heißt es void. Und das ist die Antwort auf Ihre Frage.


3
Bitte geben Sie eine Quelle für "Der ursprüngliche Grund ..." an. Meines Wissens liegt der wahre Grund darin, dass C als portabler Assembler konzipiert wurde.
Thorbjørn Ravn Andersen

6
@ ThorbjørnRavnAndersen Bitten Sie mich wirklich, eine Quelle für die Behauptung anzugeben, dass die Syntax von Java von der C-Syntax abgeleitet ist?
Mike Nakis

3
@ ThorbjørnRavnAndersen oh, ich verstehe, ich habe falsch verstanden. Sie möchten also eine Quelle für meine Behauptung über C, nicht über Java. Okay, tut mir leid, dafür habe ich keine Quelle. Aber ich denke es ist ziemlich offensichtlich. (Ich habe 1987 in Pascal programmiert, als ich zu C. gewechselt bin. Meine Güte, das ist 30 Jahre her.)
Mike Nakis

6
Es ist nicht offensichtlich. C wurde ungefähr zur gleichen Zeit wie Pascal von Leuten entwickelt, die höchstwahrscheinlich nicht einmal wussten, dass es existiert. Bitte geben Sie keine Annahmen als Fakten an.
Thorbjørn Ravn Andersen

12
Der eigentliche ursprüngliche Grund war, dass standardmäßig C-Funktionen zurückgegeben wurden int, sodass nach K & R ein Schlüsselwort hinzugefügt wurde, das angibt, dass kein Rückgabetyp vorliegt.
user207421

20

Einige Methoden, wie zum Beispiel, System.out.printlngeben nichts Nützliches zurück, sondern werden nur für diesen Nebeneffekt aufgerufen. voidist ein hilfreicher Indikator für den Compiler und den Codeleser, dass kein nützlicher Wert zurückgegeben wird.

Rückgabe nullstatt voidbedeutet, dass Sie nur NullPointerExceptionden Moment erhalten, in dem Sie diesen Wert für alles verwenden. Sie tauschen also einen Fehler bei der Kompilierung gegen einen wesentlich schlechteren Laufzeitfehler. Außerdem müssten Sie den Rückgabetyp als definieren Object, was irreführend und verwirrend wäre. (Und Verketten würde immer noch nicht funktionieren.)

Statuscodes werden normalerweise verwendet, um Fehlerzustände anzuzeigen, aber Java hat Ausnahmen für diesen Zweck.

Rückgabe thisvon statischen Methoden ist nicht möglich.

Das Zurückgeben eines generischen SuccessObjekts hätte keinen nützlichen Zweck.


1
Stimmen Sie mit Ihnen völlig überein, die einfachste und prägnanteste Antwort.
webo80

11

Ein Grund dafür ist , dass es sein kann irreführend jemals etwas zurück andere als beispielsweise null. Beispiel:

Was soll Arrays.sort(a)zurückkehren?

Wenn Sie der Meinung sind, dass adie Rückgabe erfolgen soll, damit der Anruf verkettet werden kann (was Ihrer Frage nach Ihre Antwort zu sein scheint), ist nicht mehr klar, ob der Rückgabewert eine Kopie des Originalobjekts oder des Originalobjekts selbst ist . Beides ist möglich. Und ja, Sie könnten es in die Dokumentation aufnehmen, aber es ist hinreichend vieldeutig, dass Sie die Mehrdeutigkeit nicht erst erzeugen sollten.

Wenn Sie dagegen argumentieren, dass zurückgegeben nullwerden soll, stellt sich die Frage, welche Informationen der Rückgabewert möglicherweise dem Aufrufer bereitstellt und warum der Programmierer zum Schreiben gezwungen werden sollte, return nullwenn er keine Informationen übermittelt.

Und wenn Sie etwas völlig Absurdes (wie die Länge von a) zurückgeben, ist die Verwendung dieses Rückgabewerts nur verwirrend - denken Sie nur daran, wie verwirrend es ist, int len = Arrays.sort(a)statt zu sagen int len = A.length!


1
Um ehrlich zu sein, müsste der Programmierer nicht unbedingt schreiben return null;- die JVM könnte genauso gut vorschreiben, dass, wenn die Steuerung durch eine andere Methode als eine explizite returnoder throwAnweisung vom Ende einer Funktion abweicht, dies nullimplizit an den Aufrufer zurückgegeben wird. IIRC C führt dies aus, mit der Ausnahme, dass der Rückgabewert in diesem Fall undefiniert ist (dies ist der Wert, der sich zufällig an der Stelle befindet, an der sich der Rückgabewert zu diesem Zeitpunkt befindet).
ein CVn

2
Die richtige Antwort auf Ihre fett gedruckte Frage lautet "ein sortiertes Array".
Bryan Boettcher

2
@BryanBoettcher: Hast du den Rest nicht gelesen?
Mehrdad

1
@Michael Kjörling: Es ist eine grundlegende Eigenschaft der Java-Sprache, solche Fehler zu verhindern, dh kein „undefiniertes Verhalten“ zu haben, wenn Sie eine returnAussage vergessen . Stattdessen wird ein Compilerfehler angezeigt, der Sie über Ihren Fehler informiert. Ein Implizit einzufügen return null;wäre besser als "undefiniertes Verhalten", aber nicht viel. Das wäre nur dann sinnvoll, wenn der Rückgabetyp keine Bedeutung hat, aber woher zieht der Compiler die Schlussfolgerung, dass der Rückgabewert keine Bedeutung hat, wenn er keine Bedeutung hat void?
Holger

2
@ MichaelKjörling: "Wenn das returnwirklich keinen Wert hinzufügt ..." Nun, wie gesagt, es gibt keinen Indikator für den Compiler, dass eine Rückgabe keinen Wert hinzufügt, wenn es keinen voidTyp gibt. Es ist eigentlich das Gegenteil, die erste Annahme ist, dass eine Methode, die einen Rückgabetyp deklariert, returnetwas Nützliches von diesem Typ haben möchte . Und wir haben noch nicht über primitive Typen gesprochen, die keinen nullWert haben. Daher würde jeder vom Compiler eingefügte Standardwert mit dem Bereich potenziell nützlicher Rückgabewerte in Konflikt stehen.
Holger

7

Das Zurückgeben eines, booleandas immer gleich dem ist, truewas Sie vorschlagen, ist nicht nur sinnlos (der Rückgabewert enthält keine Informationen), sondern wäre tatsächlich irreführend. Die meiste Zeit ist es am besten, Rückgabetypen zu verwenden, die nur so viele Informationen enthalten, wie tatsächlich verfügbar sind - deshalb haben Sie in Java 4 Milliarden mögliche Werte booleanfür true/ falseanstatt zurückzugeben int. Ebenso booleanwäre es irreführend , ein mit zwei möglichen Werten zurückzugeben, wenn es nur einen möglichen Wert gibt ("generischer Erfolg").

Es würde auch unnötigen Leistungsaufwand hinzufügen.

Wenn eine Methode nur für ihre Nebenwirkung verwendet wird, gibt es nichts zurückzugeben, und der Rückgabetyp gibt voidgenau das wieder. Potenzielle seltene Fehler können über Ausnahmen gemeldet werden.

Eine Sache, die verbessert werden könnte, wäre die Herstellung voideines tatsächlichen Typs wie Scala Unit. Dies würde einige Probleme wie den Umgang mit Generika lösen.


Lustige Tatsache: List.addKommt immer wieder true, aber das ist aus Gründen der Vereinbarkeit mit dem weiter gefassten Vertrag von Collection.add, also Set.adddarf trueoder wiederkommen false.
Holger

4

Java ist eine relativ alte Sprache. Und 1995 (als es erstellt wurde) und kurz danach waren die Programmierer sehr besorgt darüber, wie lange ein Prozess die Kontrolle über den Prozessor und den Speicherverbrauch übernahm. Das Zurückgeben von void würde einige Taktzyklen und ein wenig Speicherverbrauch durch einen Funktionsaufruf eliminieren, da Sie den Rückgabewert nicht auf den Stapel legen müssten und ihn anschließend nicht entfernen müssten.

Effizienter Code gibt Ihnen nichts zurück, was Sie niemals verwenden würden. Daher ist es eine viel bessere Vorgehensweise, wenn Sie eine Leerstelle in etwas einfügen, das einen bedeutungslosen Rückgabewert hat, als einen Erfolgswert zurückzugeben.


14
Programmierer, die sich große Sorgen um die Geschwindigkeit machten, würden Java heutzutage sowieso nicht verwenden, da die ersten Implementierungen von Java notorisch langsam waren. So langsam, dass viele Leute, die derzeit nicht damit arbeiten, immer noch den Eindruck haben, Java sei ein Synonym für fett und langsam.
Sarge Borsch

8
@SargeBorsch erinnert mich an einen alten Programmierwitz von vor 20 Jahren. "Klopf klopf." "Wer ist da?" ... ... ... ... sehr lange Pause ... ... "Java"
Dan Neely

4
@DanNeely und einige Zeit später stürzt ein KI-angetriebenes Auto mit voller Geschwindigkeit in eine Wand und versucht nicht einmal, Bremsen zu benutzen. Es wurde in Java geschrieben. Java bremst jetzt also nicht! (Es war ein Wortspiel in russischer Sprache, kann es nicht sehr gut auf Englisch übersetzen)
Sarge Borsch

2
@SargeBorsch Was Sie haben, funktioniert wie ein englisches Wortspiel, auch wenn es in der Übersetzung etwas an Nuance verloren hat. Und Wortspiele zu übersetzen ist wahrscheinlich schwieriger als Gedichte.
Dan Neely

3
Gute Programmierer kümmern sich immer noch um die Leistung und den Speicherverbrauch, was es auch heute noch unnötig macht, die Leute aufzufordern, den Müll zurückzugeben.
Sklivvz

2

Ich habe Argumente gelesen, die nahelegen, dass die zweite Option (Return this) der Ansatz sein sollte void. Dies ist eine ziemlich gute Idee, aber der Builder-Ansatz war zum Zeitpunkt der Erstellung von Java nicht sehr beliebt. Wenn es damals so populär war wie heute, könnte dies der Ansatz gewesen sein. Rückkehr nullist eine wirklich schlechte Idee, IMO. Ich wünschte, ich wäre überhaupt nullnicht einmal in der Sprache.

Das Problem ist nun, dass, wenn eine Methode eine Instanz ihres eigenen Typs zurückgibt, nicht klar ist, ob es sich um ein neues Objekt oder um dasselbe handelt. Oft spielt es keine Rolle (oder sollte es nicht sein), aber manchmal spielt es eine Rolle. Ich nehme an, die Sprache könnte geändert werden, um implizit thisauf leere Methoden zurückzukommen. Das einzige Problem, an das ich im Moment denken kann, ist, dass es keine Kompilierungswarnungen geben würde, wenn die Methode später geändert würde, um ein neues Objekt desselben Typs zurückzugeben, aber das ist vielleicht keine große Sache. Das aktuelle Typsystem kümmert sich jetzt nicht um diese Art von Änderungen. Wie dies mit der Vererbung / den Schnittstellen zusammenwirkt, wäre zu überlegen, würde es jedoch ermöglichen, alte APIs ohne Builder-Stil so aufzurufen, als ob dies der Fall wäre.


2
@Cody Guter Punkt, dies würde nicht auf statische Methoden zutreffen.
JimmyJames

14
@ marisbest2 Welches Argument?
8bittree

3
Nun, wenn nullnicht Teil der Sprache wäre, würden die Leute alle Arten von Sentinel-Werten verwenden, die bedeuten "Bitte ignorieren Sie dieses Argument / diesen Rückgabewert, es enthält keinen sinnvollen Wert". Das Problem dabei nullist nicht das Ding selbst, sondern nur, dass es dazu neigt, falsch benutzt zu werden. Ich würde wetten , dass dieses Problem nur übertrieben sein würde , wenn die standardisierte nullmit einer Vielzahl von kundenspezifischen Lösungen ersetzt werden würde, wobei jede Implementierung anders verhalten würde wie Nutzung leise ignorieren, oder werfen besondere Ausnahmen anstelle eines standart Null - Zeiger Ausnahme, usw.
cmaster

2
@cmaster ist ein offensichtlicher Ersatz für nulleinen geeigneten optionalen Typ (wie Maybein Haskell). Das Problem mit Nullen in Java ist, dass es keinen vernünftigen Weg gibt, sofort zu entscheiden, ob ein Wert in der Praxis nullbar ist oder nicht. Dies führt sowohl zu: unnötig defensiver Programmierung (Überprüfung auf Nullen, wo dies sinnlos ist, wodurch der Prozentsatz des Poops im Code erhöht wird) als auch zu versehentlichen NPEs in der Produktion (wenn vergessen wird, zu überprüfen, wo es benötigt wird). Ja, es wird teilweise durch Anmerkungen gelöst, aber sie sind optional, nicht immer aktiviert usw. Sie werden also nicht an Grenzen mit Code von Drittanbietern gerettet.
Sarge Borsch

2
@SargeBorsch Dem stimme ich zu :-) Mein Einwand richtet sich gegen eine Sprache ohne einheitlichen Ausdruck "Bitte ignoriere diesen Wert". nullFunktioniert hierfür gut (fine = simple), aber standardisierte optionale Werte mit fester Semantik sind definitiv eine weitere gute Option (fine = safe).
cmaster

2

Ein anderer Aspekt:

Java ist eine statische Sprache mit sehr strengen Merkmalen. Dies bedeutet, dass viele Dinge, die in anderen Sprachen ziemlich offen oder dynamisch wären (c / f Ruby, Lisp usw.), streng bestimmt sind.

Dies ist eine allgemeine Entwurfsentscheidung. Das "Warum" ist schwer zu beantworten (gut, weil die Designer der Sprache dachten, es wäre gut!). Das "Wofür" ist ziemlich klar: Es lässt den Compiler viele Fehler erkennen, was im Allgemeinen für jede Sprache ein ziemlich gutes Feature ist. Zweitens ist es vergleichsweise einfach, über die Sprache nachzudenken. Beispielsweise ist es relativ einfach, formale Korrektheit zu erstellen, die sich in (Teilmengen von) der Java-Sprache nachweist. Zum Vergleich: In einer dynamischen Sprache wie Ruby et al.

Dieses Denken durchdringt die Sprache, zum Beispiel die erzwungene Deklaration möglicher Ausnahmen, die eine Methode auslösen kann, die getrennte Art von interfacevs. class, um mehrdeutige Mehrfachvererbung zu vermeiden, und so weiter. Für das, was es ist (eine statische, zwingende OOP-Sprache in der realen Welt mit starkem Fokus auf der Fehlerbehandlung zur Kompilierungszeit), sind diese Dinge tatsächlich recht elegant und mächtig. Sie kommen theoretischen (naturwissenschaftlichen) Sprachen näher, die gezielt entwickelt wurden, um einige dieser Themen zu untersuchen, als jede andere reale Sprache zuvor (zu diesem Zeitpunkt wohlgemerkt).

Damit. Ein strikter voidTyp ist eine eindeutige Meldung: Diese Methode gibt nichts zurück, Punkt. Es ist was es ist. Das Ersetzen durch die Erzwingung, immer etwas zurückzugeben, würde entweder zu einem viel dynamischeren Verhalten führen (wie in Ruby, wo jedes Def immer einen expliziten oder impliziten Rückgabewert hat), was für die Beweisbarkeit und Argumentation schlecht wäre; oder zu massiv aufblähen, indem Sie hier einen anderen Mechanismus verwenden.

(Und NB, Ruby (zum Beispiel) handhabt dies anders und seine Lösung ist immer noch genauso akzeptabel wie die von Java, da es eine völlig andere Philosophie hat. Zum Beispiel wirft es Beweisbarkeit und Vernünftigkeit komplett aus dem Fenster, während es einen großen Fokus darauf legt extrem hohe Ausdruckskraft der Sprache.)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.