J'ai récemment rencontré une construction Java que je n'avais jamais vue auparavant et je me demandais si je devais l'utiliser. Il semble être appelé blocs d'initialisation .
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}
Le bloc de code sera copié dans chaque constructeur, c'est-à-dire que si vous avez plusieurs constructeurs, vous n'avez pas à réécrire le code.
Cependant, je vois trois principaux inconvénients en utilisant cette syntaxe:
- C'est l'un des très rares cas en Java où l'ordre de votre code est important, car vous pouvez définir plusieurs blocs de code et ils seront exécutés dans l'ordre où ils sont écrits. Cela me semble nuisible car le simple fait de changer l'ordre des blocs de code va en fait changer le code.
- Je ne vois vraiment aucun avantage à l'utiliser. Dans la plupart des cas, les constructeurs s'appelleront avec des valeurs prédéfinies. Même si ce n'est pas le cas, le code pourrait simplement être placé dans une méthode privée et appelé depuis chaque constructeur.
- Cela réduit la lisibilité, car vous pourriez mettre le bloc à la fin de la classe et le constructeur est normalement au début de la classe. Il est assez contre-intuitif de regarder une partie complètement différente d'un fichier de code si vous ne vous attendez pas à ce que ce soit nécessaire.
Si mes affirmations ci-dessus sont vraies, pourquoi (et quand) cette construction de langage a-t-elle été introduite? Existe-t-il des cas d'utilisation légitimes?
{ doStuff(); }
de la classe est un bloc d'initialisation.doStuff()
Réponses:
Il y a deux cas où j'utilise des blocs d'initialisation.
Le premier sert à initialiser les membres finaux. En Java, vous pouvez initialiser un membre final soit en ligne avec la déclaration, soit vous pouvez l'initialiser dans le constructeur. Dans une méthode, il est interdit d'assigner à un membre final.
Ceci est valable:
Ceci est également valable:
Ceci n'est pas valide:
Si vous avez plusieurs constructeurs et si vous ne pouvez pas initialiser un membre final en ligne (car la logique d'initialisation est trop complexe), ou si les constructeurs ne peuvent pas s'appeler eux-mêmes, vous pouvez soit copier / coller le code d'initialisation, soit utiliser un bloc d'initialisation.
L'autre cas d'utilisation que j'ai pour les blocs d'initialisation est pour la construction de petites structures de données d'assistance. Je déclare un membre et y mets des valeurs juste après ses déclarations dans son propre bloc d'initialisation.
la source
squareVal = val * val
se plaindront donc d'accéder aux valeurs non initialisées. Les blocs d'initialisation ne peuvent pas dépendre des arguments passés au constructeur. La solution habituelle que j'ai vue à ce genre de problème est de définir un seul constructeur "de base" avec la logique complexe, et de définir tous les autres constructeurs en termes de celui-ci. En fait, la plupart des utilisations des initialiseurs d'instance peuvent être remplacées par ce modèle.En général, n'utilisez pas de blocs d'initialisation non statiques (et évitez peut-être aussi les blocs statiques).
Syntaxe déroutante
En regardant cette question, il y a 3 réponses, mais vous avez trompé 4 personnes avec cette syntaxe. J'étais l'un d'eux et j'écris Java depuis 16 ans! De toute évidence, la syntaxe est potentiellement sujette aux erreurs! Je m'en éloignerais.
Constructeurs télescopiques
Pour des choses vraiment simples, vous pouvez utiliser des constructeurs "télescopiques" pour éviter cette confusion:
Modèle de générateur
Si vous avez besoin de doStuff () à la fin de chaque constructeur ou d'une autre initialisation sophistiquée, un modèle de générateur serait peut-être préférable. Josh Bloch énumère plusieurs raisons pour lesquelles les constructeurs sont une bonne idée. Les constructeurs prennent un peu de temps à écrire, mais correctement écrits, ils sont une joie à utiliser.
Boucles d'initialisation statique
J'utilisais beaucoup les initialiseurs statiques , mais je rencontrais parfois des boucles où 2 classes dépendaient des blocs d'initialisation statiques de l'autre avant que la classe puisse être entièrement chargée. Cela a produit un "échec de chargement de classe" ou un message d'erreur tout aussi vague. J'ai dû comparer des fichiers avec la dernière version de travail connue dans le contrôle de code source afin de comprendre quel était le problème. Pas du tout amusant.
Initialisation paresseuse
Peut-être que les initialiseurs statiques sont bons pour des raisons de performances lorsqu'ils fonctionnent et ne sont pas trop déroutants. Mais en général, je préfère l' initialisation paresseuse aux initialiseurs statiques de nos jours. Il est clair ce qu'ils font, je n'ai pas encore rencontré de bogue de chargement de classe avec eux, et ils fonctionnent dans plus de situations d'initialisation que les blocs d'initialisation.
Définition des données
Au lieu de l'initialisation statique pour la construction de structures de données, (comparer avec des exemples dans les autres réponses), j'utilise maintenant les fonctions d'assistance de définition de données immuables de Paguro :
Conculsion
Au début de Java, les blocs d'initialisation étaient le seul moyen de faire certaines choses, mais maintenant ils sont déroutants, sujets aux erreurs et, dans la plupart des cas, ont été remplacés par de meilleures alternatives (détaillées ci-dessus). Il est intéressant de connaître les blocs d'initialisation au cas où vous les verriez dans le code hérité, ou s'ils reviennent sur un test, mais si je faisais une revue de code et que j'en voyais un dans du nouveau code, je vous demanderais de justifier pourquoi aucun des Les alternatives ci-dessus étaient appropriées avant de donner votre accord.
la source
En plus de l'initialisation d'une variable d'instance qui est déclarée comme
final
(voir la réponse de barjak ), je mentionnerais également lestatic
bloc d'initialisation.Vous pouvez les utiliser comme une sorte de "constructeur statique".
De cette façon, vous pouvez effectuer des initialisations complexes sur une variable statique la première fois que la classe est référencée.
Voici un exemple inspiré de celui de barjak:
la source
En ce qui concerne les blocs d'initialisation non statiques, leur fonction nue consiste à agir comme constructeur par défaut dans les classes anonymes. C'est fondamentalement leur seul droit d'exister.
la source
Je suis totalement d'accord avec les déclarations 1, 2, 3. Je n'utilise jamais non plus d'initialiseurs de bloc pour ces raisons et je ne sais pas pourquoi il existe en Java.
Cependant, je suis obligé d'utiliser l' initialiseur de bloc statique dans un cas: quand je dois instancier un champ statique dont le constructeur peut lever une exception vérifiée.
Mais à la place, vous devez faire:
Je trouve cet idiome très moche (il vous empêche également de marquer
context
commefinal
) mais c'est le seul moyen pris en charge par Java pour initialiser de tels champs.la source
context = null;
votre bloc catch, vous pourrez peut-être déclarer le contexte comme final.The final field context may already have been assigned
static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }