Pourquoi Java a-t-il des méthodes `void`?


51

Pourquoi / pourquoi Java doit-il avoir des voidméthodes? Référence :

Toute méthode déclarée vide ne renvoie pas de valeur.

Autant que je sache, chaque utilisation de voidserait mieux servie en renvoyant un indicateur d'état, l'objet invoqué ou null.

Cela ferait de chaque appel une instruction assignable et faciliterait les modèles de constructeur et l’enchaînement des méthodes. Les méthodes qui ne sont appelées que pour leurs effets renvoient généralement un type booléen ou générique Successou lèvent une exception en cas d'échec.


Ainsi, nous n'avons pas besoin d'une syntaxe spéciale pour écrire des sous-programmes. Nous pouvons utiliser le même usage que les fonctions.
candied_orange

Réponses:


158

Parce qu'il y a une différence entre "Cette fonction peut réussir ou échouer et est suffisamment consciente d'elle-même pour pouvoir faire la différence" et "Il n'y a pas de retour d'information sur l'effet de cette fonction". Sans cela void, vous vérifieriez sans cesse les codes de réussite et croiriez que vous écrivez un logiciel robuste, alors que vous ne faites rien de la sorte.


10
Mais si cela a réussi ou échoué est quelque chose à exprimer en lançant ou en ne lançant pas une exception, en ne retournant pas un drapeau. Et rentrer voidne dit rien sur le point de savoir si la méthode ou la fonction est suffisamment consciente de soi pour pouvoir être lancée de manière appropriée.
Volker Siegel le

12
@VolkerSiegel: Le contexte de la réponse est la question. Le PO a suggéré que, dans tous les cas où la fonction void est utilisée, la fonction retourne plutôt un indicateur d'état indiquant le succès ou l'échec. Dans ce contexte, rendre nul signifie en effet que la fonction n'a littéralement rien à restituer. Forcer une telle fonction à toujours renvoyer 0 pour indiquer le succès donnera aux utilisateurs de la fonction une fausse assurance qu'ils vérifient les erreurs alors qu'en réalité, la valeur renvoyée est toujours égale à 0, indépendamment du succès ou de l'échec.
Slebetman

19
@VolkerSiegel Il existe un grand nombre de situations dans lesquelles les exceptions ne sont pas la bonne façon de faire les choses. En fait, je dirais qu'il est plus rare qu'une exception soit juste que fausse: une exception signifie qu'il se passe quelque chose d'horrible qui doit être expliqué. Un cas d'échec attendu ne doit jamais utiliser une exception.
Gabe Sechan

35
@GabeSechan Non, des exceptions doivent être levées si la méthode est incapable de remplir son contrat. Si la méthode nécessite une référence non NULL mais qu'elle en ait une, c'est un échec attendu et elle devrait être lancée.
Andy

5
Il existe de nombreuses syntaxes alternatives pour résoudre ce problème. La raison pour laquelle ils ont choisi cette solution (délicate) est que C l'utilise.
Marco van de Voort

95
  1. Parce que C a un voidtype, et Java a été conçu pour suivre de nombreuses conventions de la famille de langage C.
  2. Il existe de nombreuses fonctions pour lesquelles vous ne souhaitez pas que la valeur soit renvoyée. Qu'allez-vous faire avec "un Successtype générique " de toute façon? En fait, les valeurs renvoyées pour indiquer le succès sont encore moins importantes en Java qu'en C, car Java comporte des exceptions pour indiquer un échec, contrairement au C.

8
> "Qu'est-ce que vous allez faire avec" un type de succès générique "de toute façon?" - lors de l'écriture de code générique, un type de valeur unique (appelé type d'unité btw) est très utile car il élimine le cas particulier de fonctions qui "retournent simplement" mais ne renvoient rien en particulier. Mais lors de la création de Java, le système de types utilisé était encore moins expressif (sans paramètres de types). Il n'y avait donc pas beaucoup de différence pratique. Et maintenant, il est trop tard pour changer. Les langues plus modernes évitent en fait le "type" void (en réalité ce n'est même pas un type, mais juste un marqueur spécial pour les méthodes) et utilisent plutôt le type d'unité.
Sarge Borsch le

9
Le Voidtype de @SargeBorsch Java agit comme un type d'unité (par exemple, pour les génériques), bien qu'il soit tristement différent de void(remarque: un chaton câlin meurt chaque fois que vous inventez un nouveau type pour corriger le type que vous aviez foiré dans la version précédente). Si quelqu'un ne connaît pas des types d' unité, ceci est une intro décent: en.wikipedia.org/wiki/Unit_type
Ogre Psalm33

1
@ OgrePsalm33 il n'agit pas vraiment comme un type d'unité (du moins de façon pratique - il faut encore écrire "return null" pour retourner depuis une méthode qui a le type de retour Void, et le compilateur AFAIK alloue de l'espace sur la pile pour les références Void , ne profitant pas du fait qu’il est inutile de lire ces valeurs), c’est simplement une convention de l’utiliser lorsque le type «approprié» d’unité serait plus approprié.
Sarge Borsch

3
@immibis parce que le type d'unité, par définition, doit avoir exactement une valeur et que Void n'a pas de valeurs. Oui, il est possible d'utiliser null(en fait, c'est le seul choix), mais null est une chose spéciale, toute valeur de type référence en Java peut être nulle (ceci est une autre erreur dans la conception du langage). Et, comme je l’ai dit plus tôt, si vous utilisez le Voidtype de retour au lieu de voidmark, le compilateur Java n’est pas votre ami.
Sarge Borsch

5
@SargeBorsch Void a exactement une valeur, qui est null. Le fait qu’il nulls’agisse d’un membre de la plupart des types n’a aucune incidence sur le fait qu’il s’agisse Voidou non d’un type d’unité.
user253751

65

La raison initiale pour laquelle le langage a un voidtype est parce que, comme dans C, les créateurs du langage ne voulaient pas compliquer inutilement la syntaxe du langage avec des procédures et des fonctions identiques à celles de Pascal.

C'était la raison initiale.

Vos suggestions:

retourner un drapeau d'état

C'est un non-non. Nous n'utilisons pas d'indicateurs d'état. Si quelque chose ne va pas, nous le signalons via des exceptions.

l'objet invoqué

Le style courant des invocations est un développement récent. La plupart des programmeurs qui utilisent très bien couramment aujourd'hui et qui roulent des yeux s’ils doivent utiliser une interface qui ne la prend pas en charge ne sont même pas nés de la création de Java.

retourne null

Cela vous obligerait à déclarer une méthode comme renvoyant quelque chose, alors qu'en réalité, elle ne renvoie rien. Cela serait donc très déroutant pour quelqu'un qui cherche une interface à essayer de comprendre ce qu'elle fait. Les gens inventeraient inévitablement une classe par ailleurs inutile, qui signifie "aucune valeur de retour", de sorte que toutes les fonctions qui n'ont rien à renvoyer puissent renvoyer une référence nulle à une telle classe. Ce serait maladroit. Heureusement, il existe une solution à cela, il s'appelle void. Et c'est la réponse à votre question.


3
Veuillez fournir une source pour "La raison originale ...". À ma connaissance, la vraie raison est que C a été conçu pour être un assembleur portable.
Thorbjørn Ravn Andersen

6
@ ThorbjørnRavnAndersen êtes-vous réellement en train de me demander de fournir une source pour l'affirmation selon laquelle la syntaxe java est dérivée de la syntaxe C?
Mike Nakis

3
@ ThorbjørnRavnAndersen oh, je vois, j'ai mal compris. Donc, vous voulez une source pour mon affirmation sur C, pas sur Java. D'accord, je suis désolé, je n'ai pas de source pour cela. Mais je pense que c'est plutôt évident. (J'avais programmé en Pascal en 1987, quand je suis passé au C. Mon Dieu, cela fait 30 ans.)
Mike Nakis

6
Ce n'est pas évident. C a été développé à peu près au même moment que pascal par des gens qui ne savaient probablement même pas qu’il existait. S'il vous plaît ne pas énoncer les hypothèses comme des faits.
Thorbjørn Ravn Andersen

12
La vraie raison à l'origine était que, par défaut, les fonctions C étaient renvoyées int. Un mot-clé a donc été ajouté à la suite de K & R pour indiquer «aucun type de retour».
user207421

20

Certaines méthodes, comme System.out.printlnne retourne rien d’utile, mais s’appelle purement pour cet effet secondaire. voidest un indicateur utile pour le compilateur et pour le lecteur de code qu'aucune valeur utile n'est renvoyée.

Retourner nullau lieu de voidsignifie que vous obtiendrez simplement NullPointerExceptionle moment où vous utiliserez cette valeur pour quoi que ce soit. Donc, vous négociez une erreur de compilation pour une erreur d’exécution qui est bien pire. De plus, vous devrez définir le type de retour comme Objectétant trompeur et source de confusion. (Et l'enchaînement ne fonctionnerait toujours pas.)

Les codes d'état sont généralement utilisés pour indiquer des conditions d'erreur, mais Java a des exceptions à cette fin.

Renvoyer thisn'est pas possible à partir de méthodes statiques.

Renvoyer un Successobjet générique n'aurait aucun but utile.


1
Totalement d’accord avec vous, la réponse la plus simple et concise.
webo80

11

L' une des raisons est qu'il peut être trompeur de revenir jamais autre chose que, par exemple, null. Exemple:

Que devrait Arrays.sort(a)revenir?

Si vous soutenez que cela adoit être renvoyé afin que l'appel puisse être chaîné (ce qui semble être votre réponse d'après votre question), il n'est alors plus clair si la valeur renvoyée est une copie de l'objet d'origine ou de l'objet d'origine lui-même. . Les deux sont possibles. Et oui, vous pouvez le mettre dans la documentation, mais c'est suffisamment ambigu pour ne pas créer d'ambiguïté.

D'autre part, si vous soutenez que cela nulldevrait être renvoyé, cela soulève la question de savoir quelle information la valeur renvoyée fournit éventuellement à l'appelant et pourquoi le programmeur devrait être obligé d'écrire return nulllorsqu'il ne transmet aucune information.

Et si vous revenez quelque chose d' autre tout à fait absurde (comme la longueur a), il fait juste utiliser cette valeur de retour vraiment déroutant - il suffit de penser combien plus confondant est - à - dire au int len = Arrays.sort(a)lieu de int len = A.length!


1
En toute équité, le programmeur ne serait pas forcément nécessaire d'écrire return null;- la machine virtuelle Java pourrait tout aussi bien que si le mandat de contrôle tombe à la fin d'une fonction par autre qu'une explicite returnou throwdéclaration, nullest implicitement renvoyé à l'appelant. IIRC C fait cela, sauf que la valeur de retour n'est pas définie dans ce cas (ce sera quelle que soit la valeur qui se trouve à l'emplacement utilisé pour la valeur de retour à ce moment-là).
un CVn

2
La réponse correcte à votre question en gras est "un tableau trié".
Bryan Boettcher

2
@BryanBoettcher: Vous n'avez pas lu le reste?
Mehrdad

1
@Michael Kjörling: c'est une propriété fondamentale du langage Java d'empêcher de telles erreurs, c'est-à-dire de ne pas avoir de «comportement indéfini» si vous oubliez une returninstruction. Au lieu de cela, vous obtenez une erreur du compilateur vous informant de votre erreur. Insertion d'un implicite return null;serait mieux que "comportement indéfini", mais pas beaucoup. Cela ne serait utile que lorsque le type de retour n'a pas de sens, mais d'où le compilateur tire-t-il la conclusion que la valeur de retour n'a pas de sens, quand ce n'est pas le cas void?
Holger

2
@ MichaelKjörling: «Si returncela n'ajoute vraiment aucune valeur…», eh bien, comme dit, il n'y a aucun indicateur pour le compilateur qu'un retour n'apporterait aucune valeur s'il n'y avait pas de voidtype. C'est en fait le contraire. La première hypothèse est qu'une méthode déclarant un type de retour veut returnquelque chose d'utile de ce type. Et nous n'avons pas encore parlé de types primitifs, qui n'ont pas de nullvaleur. Par conséquent, toute valeur par défaut injectée par le compilateur entrerait en conflit avec la plage de valeurs de retour potentiellement utiles.
Holger

7

Renvoyer un booleanqui est toujours égal à truece que vous suggérez est non seulement inutile (la valeur de retour ne contient aucune information), mais serait en réalité trompeur. La plupart du temps, il est préférable d'utiliser les types de retour qui ne portent que le plus d' informations est en fait disponible - c'est pourquoi en Java que vous avez booleanpour true/ falseplutôt que de retourner un intavec 4 milliards de valeurs possibles. De même, renvoyer un booleanavec deux valeurs possibles où il n'y a qu'une seule valeur possible ("succès générique") serait trompeur.

Cela ajouterait également une surcharge de performances inutile.

Si une méthode est utilisée uniquement pour ses effets secondaires, il n'y a rien à renvoyer, et le type de retour voidreflète exactement cela. Les rares erreurs potentielles peuvent être signalées via des exceptions.

Une chose qui pourrait être améliorée, serait de créer voidun type réel, tel que celui de Scala Unit. Cela résoudrait certains problèmes tels que la gestion des génériques.


Fait amusant: List.addrevient toujours true, mais c'est pour être compatible avec le contrat plus large de Collection.add, Set.addpeut donc revenir trueou false.
Holger

4

Java est une langue relativement ancienne. Et en 1995 (date de sa création) et peu de temps après, les programmeurs étaient très préoccupés par la durée pendant laquelle un processus prenait le contrôle du processeur ainsi que la consommation de mémoire. Le renvoi de vide éliminerait quelques cycles d'horloge et un peu de consommation de mémoire d'un appel de fonction, car vous n'auriez pas à mettre la valeur de retour sur la pile et vous ne seriez pas obligé de la supprimer par la suite.

Un code efficace ne vous restitue pas quelque chose que vous n'utiliseriez jamais. Par conséquent, mettre vide dans un objet qui a une valeur de retour sans signification est une bien meilleure pratique que de renvoyer une valeur de succès.


14
Les programmeurs qui étaient très préoccupés par la vitesse n’utiliseraient de toute façon pas Java de nos jours, car les premières applications de Java étaient notoirement lentes. Si lent que beaucoup de gens qui ne travaillent pas actuellement avec lui ont toujours l'impression que Java est synonyme de gras et lent.
Sarge Borsch le

8
@SargeBorsch me rappelle une vieille blague sur la programmation d'il y a 20 ans. "Toc Toc." "Qui est là?" ... ... ... ... très longue pause ... ... "Java"
Dan Neely

4
@DanNeely et peu de temps après, une voiture conduite par l'IA s'écrase à pleine vitesse dans un mur, sans même essayer de freiner. C'était écrit en Java. Donc, Java ne freine pas maintenant! (C'était un jeu de mots en russe, je ne peux pas très bien le traduire en anglais)
Sarge Borsch

2
@SargeBorsch Ce que vous avez fonctionne comme un jeu de mots anglais, même s'il a perdu quelques nuances dans la traduction. Et traduire des jeux de mots est probablement plus difficile que la poésie.
Dan Neely

3
Les bons programmeurs sont toujours préoccupés par les performances et la consommation de mémoire, ce qui oblige inutilement les personnes à renvoyer les ordures pertinentes, même aujourd'hui.
Sklivvz

2

J'ai lu des arguments qui suggèrent que la deuxième option (retour this) devrait être l'approche au lieu de void. C'est une bonne idée, mais l'approche de type constructeur n'était pas populaire au moment de la création de Java. Si c'était aussi populaire à l'époque que maintenant, c'est peut-être l'approche qui a été adoptée. Revenir nullest une très mauvaise idée, OMI. Je souhaite nulln'était même pas dans la langue du tout.

Le problème est maintenant que si une méthode retourne une instance de son propre type, il n'est pas clair s'il s'agit d'un nouvel objet ou du même objet. Souvent, cela n'a pas d'importance (ou ne devrait pas l'être), mais d'autres fois, cela compte. Je suppose que le langage pourrait être modifié pour revenir implicitement thissur les méthodes void. Le seul problème auquel je peux penser pour le moment est que si la méthode était modifiée ultérieurement pour renvoyer un nouvel objet du même type, il n'y aurait pas d'avertissement de compilation, mais ce n'est peut-être pas grave. Le système de types actuel ne se soucie pas de ces types de changements maintenant. La manière dont cela interagit avec l'héritage / les interfaces nécessiterait une certaine réflexion, mais permettrait également aux anciennes API sans style constructeur d'être appelées comme si elles le faisaient.


2
@Cody Bon point, cela ne s'appliquerait pas aux méthodes statiques.
JimmyJames

14
@ marisbest2 Quel argument?
8bittree

3
Eh bien, s’ils nullne faisaient pas partie du langage, les gens utiliseraient toutes sortes de valeurs sentinelles qui signifient "ignorez cet argument / cette valeur de retour, il ne contient aucune valeur sensible". Le problème avec nulln'est pas la chose elle-même, mais seulement qu'elle a tendance à être utilisée de manière incorrecte. Je parierais que ce problème ne serait exagéré que si la norme nullétait remplacée par une multitude de solutions personnalisées, dans lesquelles chaque implémentation se comporterait différemment, comme en ignorant silencieusement l'utilisation ou en lançant des exceptions spéciales au lieu d'une exception standart null pointer, etc.
cmaster

2
@cmaster est un remplacement évident pour nullun type optionnel approprié (comme Maybedans Haskell). Le problème avec les valeurs NULL en Java est qu’il n’ya pas de moyen sensé de décider immédiatement si une valeur est nullable en pratique ou non. Cela entraîne à la fois une programmation inutilement défensive (recherche de valeurs nulles là où c'est inutile, augmentant ainsi le pourcentage de caca dans le code) et de NPE accidentels en production (si on oublie de vérifier où cela était nécessaire). Oui, cela est partiellement résolu par les annotations mais elles sont facultatives, pas toujours cochées, etc. Elles ne vous sauveront donc pas aux limites avec du code tiers.
Sarge Borsch

2
@SargeBorsch Je suis d'accord avec cela :-) Mon objection est contre une langue sans moyen standard d'exprimer un "s'il vous plaît ignorer cette valeur". nullfonctionne très bien pour cela (fin = simple), mais les valeurs optionnelles normalisées avec une sémantique solide sont définitivement une autre bonne option (fin = sans danger).
cmaster

2

Un autre aspect:

Java est un langage statique avec des fonctionnalités assez strictes. Cela signifie que beaucoup de choses qui seraient assez ouvertes ou dynamiques dans d'autres langues (c / f Ruby, Lisp, etc.) sont strictement déterminées.

Ceci est une décision de conception générale. Il est difficile de répondre au "pourquoi" (enfin, car les concepteurs de la langue ont pensé que ce serait bien!). Le "pour quoi" est assez clair: il permet au compilateur de détecter beaucoup d'erreurs, ce qui est généralement une très bonne fonctionnalité pour toutes les langues. Deuxièmement, il est relativement facile de raisonner sur la langue. Par exemple, il est relativement facile de créer des preuves de correction formelles dans (des sous-ensembles de) le langage Java; à titre de comparaison, cela serait pratiquement impossible dans un langage dynamique comme Ruby et al.

Cette pensée imprègne le langage, par exemple, la déclaration forcée d'exceptions possibles qu'une méthode peut générer, le type séparé de interfacevs classpour éviter un héritage multiple ambigu, etc. Pour ce qu’il est (langage du monde réel impératif statique et axé sur la gestion des erreurs de compilation), ces éléments sont en fait assez élégants et puissants. Ils se rapprochent des langages théoriques (scientifiques) qui sont conçus exprès pour explorer certaines de ces questions plus que tout autre langage du monde réel auparavant (à ce moment-là, pensez-vous).

Alors. Avoir un voidtype strict est un message clair: cette méthode ne retourne rien, point. C'est ce que c'est. Le remplacer par l'application forcée de toujours renvoyer quelque chose mènerait soit à un comportement beaucoup plus dynamique (comme en Ruby où chaque def a une valeur de retour explicite ou implicite, toujours), ce qui serait mauvais pour la prouvabilité et le raisonnement; ou à gonfler massif en utilisant un autre mécanisme ici.

(Et NB, Ruby (par exemple) gère différemment, et sa solution est toujours exactement comme acceptable que de Java, car il a une philosophie complètement différente. Par exemple , il jette prouvabilité et raisonnabilité complètement hors de la fenêtre tout en mettant un grand accent sur extrêmement haute expressivité de la langue.)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.