Y avait-il une raison pour laquelle les concepteurs de Java estimaient que les variables locales ne devraient pas avoir de valeur par défaut? Sérieusement, si les variables d'instance peuvent recevoir une valeur par défaut, pourquoi ne pouvons-nous pas faire de même pour les variables locales?
Et cela conduit également à des problèmes comme expliqué dans ce commentaire à un article de blog :
Eh bien, cette règle est la plus frustrante lorsque vous essayez de fermer une ressource dans un bloc final. Si j'instancie la ressource à l'intérieur d'un essai, mais que j'essaie de la fermer dans le enfin, j'obtiens cette erreur. Si je déplace l'instanciation en dehors de l'essai, j'obtiens une autre erreur indiquant qu'il doit être dans un essai.
Très frustrant.
la source
Réponses:
Les variables locales sont principalement déclarées pour effectuer des calculs. C'est donc la décision du programmeur de définir la valeur de la variable et il ne devrait pas prendre de valeur par défaut. Si le programmeur, par erreur, n'a pas initialisé une variable locale et qu'il prend la valeur par défaut, alors la sortie pourrait être une valeur inattendue. Donc, dans le cas de variables locales, le compilateur demandera au programmeur de s'initialiser avec une valeur avant d'accéder à la variable pour éviter l'utilisation de valeurs non définies.
la source
Le "problème" auquel vous vous connectez semble décrire cette situation:
La plainte du commentateur est que le compilateur rechigne à la ligne dans la
finally
section, affirmant que celaso
pourrait être non initialisé. Le commentaire mentionne ensuite une autre façon d'écrire le code, probablement quelque chose comme ceci:Le commentateur n'est pas satisfait de cette solution car le compilateur dit alors que le code «doit être dans un essai». Je suppose que cela signifie qu'une partie du code peut soulever une exception qui n'est plus gérée. Je ne suis pas sûr. Aucune des deux versions de mon code ne gère d'exceptions, donc tout ce qui est lié aux exceptions dans la première version devrait fonctionner de la même manière dans la seconde.
Quoi qu'il en soit, cette deuxième version du code est la bonne façon de l'écrire. Dans la première version, le message d'erreur du compilateur était correct. La
so
variable n'est peut-être pas initialisée. En particulier, si leSomeObject
constructeur échoue,so
il ne sera pas initialisé, et donc ce sera une erreur de tenter d'appelerso.CleanUp
. Entrez toujours dans latry
section après avoir acquis la ressource que lafinally
section finalise.Le bloc
try
-finally
après l'so
initialisation n'est là que pour protéger l'SomeObject
instance, pour s'assurer qu'elle est nettoyée quoi qu'il arrive. S'il y a d' autres choses qui ont besoin de courir, mais ils ne sont pas liés si l'SomeObject
instance a été attribué la propriété, alors ils devraient aller dans un autretry
-finally
bloc, probablement celui qui enveloppe celui que je l' ai montré.Le fait d'exiger que les variables soient affectées manuellement avant leur utilisation ne pose pas de réels problèmes. Cela ne conduit qu'à des tracas mineurs, mais votre code sera meilleur pour cela. Vous aurez des variables ayant une portée plus limitée, et
try
- lesfinally
blocs qui ne cherchent pas à protéger trop.Si les variables locales avaient des valeurs par défaut, alors
so
dans le premier exemple aurait éténull
. Cela n'aurait vraiment rien résolu. Au lieu d'obtenir une erreur de compilation dans lefinally
bloc, vous auriez une erreurNullPointerException
cachée là-bas qui pourrait cacher toute autre exception qui pourrait se produire dans la section «Faites un peu de travail ici» du code. (Ou est-ce que les exceptions dans lesfinally
sections s'enchaînent automatiquement à l'exception précédente? Je ne me souviens pas. Même dans ce cas, vous auriez une exception supplémentaire par rapport à la vraie.)la source
De plus, dans l'exemple ci-dessous, une exception peut avoir été levée à l'intérieur de la construction SomeObject, auquel cas la variable 'so' serait nulle et l'appel à CleanUp lèvera une NullPointerException
Ce que j'ai tendance à faire, c'est ceci:
la source
Notez que l'instance finale / les variables membres ne sont pas initialisées par défaut. Parce que ceux-ci sont définitifs et ne peuvent pas être modifiés dans le programme par la suite. C'est la raison pour laquelle Java ne leur donne aucune valeur par défaut et force le programmeur à l'initialiser.
En revanche, les variables membres non finales peuvent être modifiées ultérieurement. Par conséquent, le compilateur ne les laisse pas rester non initialisés, précisément, car ils peuvent être modifiés plus tard. En ce qui concerne les variables locales, la portée des variables locales est beaucoup plus étroite. Le compilateur sait quand il est utilisé. Par conséquent, forcer le programmeur à initialiser la variable a du sens.
la source
La réponse réelle à votre question est que les variables de méthode sont instanciées en ajoutant simplement un nombre au pointeur de pile. Les remettre à zéro serait une étape supplémentaire. Pour les variables de classe, elles sont placées en mémoire initialisée sur le tas.
Pourquoi ne pas faire un pas supplémentaire? Prenez du recul - Personne n'a mentionné que «l'avertissement» dans ce cas est une très bonne chose.
Vous ne devriez jamais initialiser votre variable à zéro ou à null lors de la première passe (lorsque vous la codez pour la première fois). Affectez-le à la valeur réelle ou ne l'attribuez pas du tout, car si vous ne le faites pas, java peut vous dire quand vous vous trompez vraiment. Prenons la réponse d'Electric Monk comme un excellent exemple. Dans le premier cas, il est en fait incroyablement utile de vous dire que si le try () échoue parce que le constructeur de SomeObject a lancé une exception, vous vous retrouverez avec un NPE dans le fichier. Si le constructeur ne peut pas lever d'exception, il ne devrait pas être dans l'essai.
Cet avertissement est un formidable vérificateur de mauvais programmeur multi-chemins qui m'a sauvé de faire des choses stupides car il vérifie chaque chemin et s'assure que si vous avez utilisé la variable dans un chemin, vous deviez l'initialiser dans chaque chemin qui y mène . Je n'initialise jamais explicitement les variables jusqu'à ce que je détermine que c'est la bonne chose à faire.
En plus de cela, n'est-il pas préférable de dire explicitement "int size = 0" plutôt que "int size" et faire comprendre au prochain programmeur que vous avez l'intention qu'il soit nul?
D'un autre côté, je ne peux pas trouver une seule raison valable pour que le compilateur initialise toutes les variables non initialisées à 0.
la source
Je pense que l'objectif principal était de maintenir la similitude avec C / C ++. Cependant, le compilateur détecte et vous avertit de l'utilisation de variables non initialisées qui réduiront le problème à un point minimal. Du point de vue des performances, il est un peu plus rapide de vous permettre de déclarer des variables non initialisées car le compilateur n'aura pas à écrire une instruction d'affectation, même si vous écrasez la valeur de la variable dans l'instruction suivante.
la source
(Il peut sembler étrange de publier une nouvelle réponse si longtemps après la question, mais un doublon est apparu.)
Pour moi, la raison se résume à ceci: le but des variables locales est différent de celui des variables d'instance. Les variables locales sont là pour être utilisées dans le cadre d'un calcul; les variables d'instance sont là pour contenir l'état. Si vous utilisez une variable locale sans lui affecter de valeur, c'est presque certainement une erreur de logique.
Cela dit, je pourrais tout à fait prendre du retard en exigeant que les variables d'instance soient toujours explicitement initialisées; l'erreur se produirait sur n'importe quel constructeur où le résultat autorise une variable d'instance non initialisée (par exemple, non initialisée à la déclaration et non dans le constructeur). Mais ce n'est pas la décision Gosling, et. al., pris au début des années 90, alors nous y sommes. (Et je ne dis pas qu'ils ont fait le mauvais appel.)
je pouvais Cependant, pas me mettre à l'écart des variables locales par défaut. Oui, nous ne devrions pas compter sur des compilateurs pour vérifier notre logique, et ce n'est pas le cas, mais c'est toujours pratique lorsque le compilateur en détecte un. :-)
la source
Il est plus efficace de ne pas initialiser les variables, et dans le cas des variables locales, il est prudent de le faire, car l'initialisation peut être suivie par le compilateur.
Dans les cas où vous avez besoin d'initialiser une variable, vous pouvez toujours le faire vous-même, ce n'est donc pas un problème.
la source
L'idée derrière les variables locales est qu'elles n'existent que dans la portée limitée pour laquelle elles sont nécessaires. En tant que tel, il devrait y avoir peu de raisons d'incertitude quant à la valeur, ou du moins, à l'origine de cette valeur. Je pourrais imaginer de nombreuses erreurs résultant du fait d'avoir une valeur par défaut pour les variables locales.
Par exemple, considérons le code simple suivant ... ( NB supposons à des fins de démonstration que les variables locales se voient attribuer une valeur par défaut, comme spécifié, si elles ne sont pas explicitement initialisées )
En fin de compte, en supposant que le compilateur attribue une valeur par défaut de '\ 0' à letterGrade , ce code tel qu'il est écrit fonctionnerait correctement. Cependant, que se passe-t-il si nous oublions la déclaration else?
Un test de notre code peut entraîner les résultats suivants
Ce résultat, bien que prévisible, n'était certainement pas l'intention du codeur. En effet, probablement dans la grande majorité des cas (ou du moins dans un nombre significatif de ceux-ci), la valeur par défaut ne serait pas la valeur souhaitée , donc dans la grande majorité des cas, la valeur par défaut entraînerait une erreur. Il est plus logique de forcer le codeur à attribuer une valeur initiale à une variable locale avant de l'utiliser, car le problème de débogage causé par l'oubli du
= 1
in l'for(int i = 1; i < 10; i++)
emporte de loin sur la commodité de ne pas avoir à inclure le= 0
infor(int i; i < 10; i++)
.Il est vrai que les blocs try-catch-finally pourraient devenir un peu désordonnés (mais ce n'est pas en fait un catch-22 comme la citation semble le suggérer), quand par exemple un objet lève une exception vérifiée dans son constructeur, mais pour une raison ou autre, quelque chose doit être fait à cet objet à la fin du bloc en enfin. Un exemple parfait de cela est lorsqu'il s'agit de ressources, qui doivent être fermées.
Une façon de gérer cela dans le passé pourrait être comme ça ...
Cependant, à partir de Java 7, ce bloc finally n'est plus nécessaire en utilisant try-with-resources, comme ça.
Cela dit, (comme son nom l'indique), cela ne fonctionne qu'avec des ressources.
Et bien que le premier exemple soit un peu dégoûtant, cela en dit peut-être plus sur la façon dont try-catch-finally ou ces classes sont implémentées que sur les variables locales et la façon dont elles sont implémentées.
Il est vrai que les champs sont initialisés à une valeur par défaut, mais c'est un peu différent. Quand vous dites, par exemple,
int[] arr = new int[10];
dès que vous avez initialisé ce tableau, l'objet existe en mémoire à un emplacement donné. Supposons un instant qu'il n'y a pas de valeurs par défaut, mais à la place la valeur initiale est n'importe quelle série de 1 et de 0 se trouvant dans cet emplacement de mémoire pour le moment. Cela pourrait conduire à un comportement non déterministe dans un certain nombre de cas.Supposons que nous ayons ...
Il serait parfaitement possible que cela
Same.
puisse être affiché dans une course etNot same.
pourrait être affiché dans un autre. Le problème peut devenir encore plus grave une fois que vous commencez à parler des variables de référence.Selon la définition, chaque élément de s doit pointer vers une chaîne (ou est nul). Cependant, si la valeur initiale est n'importe quelle série de 0 et de 1 se produisant à cet emplacement de mémoire, non seulement il n'y a aucune garantie que vous obtiendrez les mêmes résultats à chaque fois, mais il n'y a pas non plus de garantie que l'objet s [0] pointe to (en supposant que cela indique quelque chose de significatif) est même une chaîne (peut-être que c'est un lapin,: p )! Ce manque de souci du type irait à l'encontre de pratiquement tout ce qui fait de Java Java. Ainsi, bien que les valeurs par défaut des variables locales puissent être considérées au mieux comme facultatives, avoir des valeurs par défaut pour les variables d'instance est plus proche d'une nécessité .
la source
si je ne me trompe pas, une autre raison pourrait être
Donner la valeur par défaut des variables membres fait partie du chargement de classe
Le chargement de classe est une chose au moment de l'exécution en java signifie que lorsque vous créez un objet, la classe est chargée avec le chargement de classe seules les variables membres sont initialisées avec la valeur par défaut JVM ne prend pas le temps de donner une valeur par défaut à vos variables locales car certaines méthodes ne le seront jamais appelé parce que l'appel de méthode peut être conditionnel, alors pourquoi prendre le temps de leur donner la valeur par défaut et réduire les performances si ces valeurs par défaut ne seront jamais utilisées.
la source
Eclipse vous donne même des avertissements de variables non initialisées, donc cela devient tout à fait évident de toute façon. Personnellement, je pense que c'est une bonne chose que ce soit le comportement par défaut, sinon votre application peut utiliser des valeurs inattendues, et au lieu que le compilateur lève une erreur, il ne fera rien (mais peut-être donner un avertissement) et vous gratterez votre tête à savoir pourquoi certaines choses ne se comportent pas tout à fait comme elles le devraient.
la source
Les variables locales sont stockées sur une pile, mais les variables d'instance sont stockées sur le tas, il y a donc quelques chances qu'une valeur précédente sur la pile soit lue au lieu d'une valeur par défaut comme cela se produit dans le tas. Pour cette raison, le jvm ne permet pas d'utiliser une variable locale sans l'initialiser.
la source
La variable d'instance aura des valeurs par défaut mais les variables locales ne peuvent pas avoir de valeurs par défaut. Puisque les variables locales sont essentiellement des méthodes / comportements, son objectif principal est de faire des opérations ou des calculs. Par conséquent, il n'est pas judicieux de définir des valeurs par défaut pour les variables locales. Sinon, il est très difficile et chronophage de vérifier les raisons des réponses inattendues.
la source
La réponse est que les variables d'instance peuvent être initialisées dans le constructeur de classe ou dans n'importe quelle méthode de classe, mais dans le cas de variables locales, une fois que vous avez défini tout ce qui dans la méthode reste à jamais dans la classe.
la source
Je pourrais penser à 2 raisons suivantes
la source