Pourquoi les variables locales ne sont-elles pas initialisées en Java?

Y avait-il une raison pour laquelle les concepteurs de Java estimaient que les variables locales ne devaient pas recevoir de valeur par défaut? Sérieusement, si les variables d’instance peuvent recevoir une valeur par défaut, alors pourquoi ne pouvons-nous pas faire la même chose 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 très 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’essaye de la fermer au final, j’obtiens cette erreur. Si je déplace l’instanciation en dehors de l’essai, j’obtiens une autre erreur indiquant que ça doit être dans un essai.

Très frustrant.

Les variables locales sont déclarées principalement pour effectuer des calculs. C’est donc la décision du programmeur de définir la valeur de la variable et elle ne devrait pas prendre de valeur par défaut. Si le programmeur, par erreur, n’a pas initialisé une variable locale et que celle-ci prend la valeur par défaut, alors la sortie peut avoir une valeur inattendue. Donc, en cas de variables locales, le compilateur demandera au programmeur d’initialiser avec une valeur avant d’accéder à la variable pour éviter l’utilisation de valeurs non définies.

Le “problème” auquel vous vous connectez semble décrire cette situation:

SomeObject so; try { // Do some work here ... so = new SomeObject(); so.DoUsefulThings(); } finally { so.CleanUp(); // Comstackr error here } 

La plainte du commentateur est que le compilateur rechigne à la ligne dans la section finally , affirmant que so pourrait être non initialisé. Le commentaire mentionne alors une autre manière d’écrire le code, probablement quelque chose comme ceci:

 // Do some work here ... SomeObject so = new SomeObject(); try { so.DoUsefulThings(); } finally { so.CleanUp(); } 

Le commentateur n’est pas satisfait de cette solution car le compilateur dit alors que le code “doit être dans une tentative”. Je suppose que cela signifie qu’une partie du code peut générer 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 les exceptions, donc tout élément lié à une exception 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 manière correcte de l’écrire. Dans la première version, le message d’erreur du compilateur était correct. La variable so peut être non initialisée. En particulier, si le constructeur de SomeObject échoue, so ne sera so pas initialisé et il sera donc so.CleanUp tenter d’appeler so.CleanUp . Entrez toujours la section try après avoir acquis la ressource que la section finally finalise.

Le bloc tryfinally après l’initialisation est uniquement là pour protéger l’instance SomeObject , afin de s’assurer qu’il soit nettoyé, quoi qu’il arrive d’autre. S’il y a d’ autres choses à exécuter, mais qu’elles ne sont pas liées à la SomeObject occurrence SomeObject , elles doivent alors aller dans un autre bloc tryfinally , probablement celui qui a été affiché.

Exiger que les variables soient assignées manuellement avant utilisation n’entraîne pas de problèmes réels. Cela ne mène qu’à des problèmes mineurs, mais votre code le sera mieux. Vous aurez des variables avec une scope plus limitée, et tryfinally blocs qui ne tentent pas de trop protéger.

Si les variables locales avaient des valeurs par défaut, alors dans le premier exemple aurait été null . Cela n’aurait vraiment rien résolu. Au lieu d’obtenir une erreur de compilation dans le bloc finally , vous auriez une NullPointerException cachée qui pourrait masquer toute autre exception pouvant se produire dans la section “Travailler ici” du code. (Ou bien les exceptions dans finally sections sont-elles automatiquement liées à l’exception précédente? Je ne m’en souviens pas. Même ainsi, vous auriez une exception supplémentaire par rapport à la vraie.)

De plus, dans l’exemple ci-dessous, une exception peut avoir été générée dans la construction SomeObject, auquel cas la variable ‘so’ serait nulle et l’appel à CleanUp lancera une exception NullPointerException

 SomeObject so; try { // Do some work here ... so = new SomeObject(); so.DoUsefulThings(); } finally { so.CleanUp(); // Comstackr error here } 

Ce que j’ai tendance à faire, c’est ceci:

 SomeObject so = null; try { // Do some work here ... so = new SomeObject(); so.DoUsefulThings(); } finally { if (so != null) { so.CleanUp(); // safe } } 

Notez que les variables d’instance / membre finales ne sont pas initialisées par défaut. Parce que ceux-ci sont définitifs et ne peuvent plus être modifiés dans le programme par la suite. C’est la raison pour laquelle Java ne donne aucune valeur par défaut pour eux 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 non initialisées, précisément, car celles-ci peuvent être modifiées ultérieurement. En ce qui concerne les variables locales, la scope des variables locales est beaucoup plus étroite. Le compilateur sait quand il est utilisé. Forcer le programmeur à initialiser la variable est donc logique.

La réponse à votre question est que les variables de méthode sont instanciées en ajoutant simplement un numéro au pointeur de la stack. Les mettre à zéro serait une étape supplémentaire. Pour les variables de classe, elles sont placées dans la mémoire initialisée sur le tas.

Pourquoi ne pas faire le pas supplémentaire? Prendre du recul – Personne n’a mentionné que “l’avertissement” dans ce cas est une très bonne chose.

Vous ne devez jamais initialiser votre variable à zéro ou à null au premier passage (lorsque vous le codez pour la première fois). Soit l’atsortingbuer à la valeur réelle, soit ne pas l’assigner du tout, car si vous ne le faites pas, java peut vous dire quand vous boussez vraiment. Prenez la réponse de Elecsortingc Monk comme un excellent exemple. Dans le premier cas, il est en fait étonnamment utile de vous dire que si try () échoue parce que le constructeur de SomeObject a créé une exception, vous vous retrouvez avec un NPE dans le fichier final. Si le constructeur ne peut pas lancer une exception, cela ne devrait pas être le cas.

Cet avertissement est un super vérificateur de mauvais programmeur multi-chemin qui m’a évité de faire des choses stupides puisqu’il vérifie chaque chemin et fait en sorte que si vous utilisiez la variable dans un chemin, vous deviez l’initialiser dans chaque chemin qui y mène . Je n’initialise plus 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 de faire comprendre au programmeur suivant que vous voulez que ce soit zéro?

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.

(Cela peut sembler étrange de poster une nouvelle réponse si longtemps après la question, mais un doublon est apparu.)

Pour moi, la raison en est la suivante: le but des variables locales est différent de celui des variables d’instance. Les variables locales doivent ê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 atsortingbuer une valeur, c’est presque certainement une erreur logique.

Cela dit, je pouvais tout à fait exiger 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 de Gosling, et. al., a pris au début des années 90, alors nous y voilà. (Et je ne dis pas qu’ils ont fait le mauvais appel.)

Cependant, je ne pouvais pas obtenir les variables locales par défaut. Oui, nous ne devrions pas compter sur les compilateurs pour vérifier notre logique, mais ce n’est pas le cas, mais cela rest pratique lorsque le compilateur en extrait un. 🙂

Je pense que le but principal était de maintenir la similarité avec C / C ++. Cependant, le compilateur détecte et vous avertit de l’utilisation de variables non initialisées, ce qui réduira le problème à un minimum. Du sharepoint 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 besoin d’écrire une instruction d’affectation, même si vous écrasez la valeur de la variable dans l’instruction suivante.

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’une variable à initialiser, vous pouvez toujours le faire vous-même, ce n’est donc pas un problème.

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 émette une erreur, il ne fera rien (mais peut-être un avertissement) Votre tête est de savoir pourquoi certaines choses ne se comportent pas comme elles le devraient.

Les variables locales sont stockées sur une stack, mais les variables d’instance sont stockées sur le tas, il est donc possible qu’une valeur précédente de la stack soit lue au lieu d’une valeur par défaut, comme cela se produit dans le tas. Pour cette raison, jvm ne permet pas d’utiliser une variable locale sans l’initialiser.

La variable d’instance aura des valeurs par défaut mais les variables locales ne peuvent pas avoir de valeurs par défaut. Comme les variables locales se trouvent essentiellement dans les méthodes / comportements, leur objective principal est d’effectuer des opérations ou des calculs. Par conséquent, il est déconseillé de définir des valeurs par défaut pour les variables locales. Sinon, il est très difficile et long de vérifier les raisons des réponses inattendues.

La réponse est que les variables d’instance peuvent être initialisées dans le constructeur de classe ou dans toute méthode de classe, mais dans le cas de variables locales, une fois que vous avez défini quoi que ce soit dans la méthode, elle rest pour toujours dans la classe.

Je pourrais penser à deux raisons suivantes

  1. Comme la plupart des réponses l’a indiqué en imposant l’initialisation de la variable locale, on s’assure que la variable locale se voit assigner une valeur en fonction des besoins du programmeur et que les résultats attendus sont calculés.
  2. Les variables d’instance peuvent être masquées en déclarant des variables locales (même nom) – pour garantir un comportement attendu, les variables locales doivent être initialisées en une valeur. (Serait totalement éviter cela cependant)