Inconvénients des objets immuables en Java?
les avantages des objets immuables en Java semblent clairs:
- état cohérent
- sécurité automatique du filetage
- simplicité
vous pouvez favoriser l'immutabilité en utilisant les champs finaux privés et l'injection du constructeur.
mais, quels sont les inconvénients à favoriser les objets immuables en Java?
c'est à dire
- incompatibilité avec L'ORM ou les outils de présentation web?
- Inflexible de conception?
- la mise en Œuvre complexités?
est-il possible de concevoir un système à grande échelle (graphe d'objets profonds) qui utilise principalement des objets immuables?
17 réponses
Mais, quels sont les inconvénients à favoriser les objets immuables en Java? incompatibilité avec ORM ou web outils de présentation?
les cadres à base de réflexion sont compliqués par des objets immuables car ils nécessite injection du constructeur:
- il n'y a pas D'arguments par défaut en Java, ce qui nous oblige à toujours fournir toutes les dépendances nécessaires
- constructeur substituant peut être salissant
- les noms d'arguments du constructeur ne sont généralement pas disponibles par réflexion, ce qui nous force à dépendre de l'ordre des arguments pour la résolution de la dépendance
la mise en Œuvre complexités?
créer des objets immuables est encore une tâche ennuyeuse; le compilateur doit s'occuper des détails de mise en œuvre, comme dans groovy
est-il possible de concevoir un système à grande échelle (graphe d'objets profonds) qui utilise principalement des objets immuables?
Certainement oui; les objets immuables font de grands blocs de construction pour d'autres objets (ils favorisent la composition) car il est beaucoup plus facile de maintenir l'invariant d'un objet complexe quand vous pouvez compter sur ses composants immuables. Le seul vrai inconvénient pour moi est de créer de nombreux objets temporaires (par ex. String concat a été un problème dans le passé ).
avec immutabilité, chaque fois que vous avez besoin de modifier des données, vous devez créer un nouvel objet. Cela peut être coûteux.
Imaginez avoir besoin de modifier un bit dans un objet qui consomme plusieurs mégaoctets de mémoire: il vous faudrait instancier un tout nouvel objet, allouer de la mémoire, etc. Si vous devez le faire plusieurs fois, la mutabilité devient très attrayante.
si vous optez pour la mutabilité, alors vous constaterez que chaque fois que vous devez appeler une méthode que vous ne voulez pas que l'objet change, ou que vous devez retourner un objet qui fait partie de l'état interne, vous devez faire une copie défensive.
si vous regardez vraiment les programmes qui utilisent des objets mutables, vous constaterez qu'ils sont susceptibles d ' "attaquer" en modifiant:
- objets passés aux constructeurs
- objets passés aux méthodes
- les objets retournés par les méthodes.
le problème n'apparaît pas très souvent parce que la plupart des programmes ne changent pas les données (ils sont en réalité immuables en vertu d'eux ne changeant jamais).
je fais personnellement tout ce que je peux éventuellement finale. J'ai probablement 90%-95% de l'ensemble des variables (paramètres, les locaux, l'instance, la statique, les exceptions, etc...) marqué comme final. Il y a certains cas où il doit être mutables, mais la grande majorité des cas, il n'a pas.
ça dépend de votre concentration. Si vous écrivez des bibliothèques pour des tiers à utiliser vous pensez à cela beaucoup plus que si vous écrivez une application que seulement vous (ou votre équipe) maintiendra.
je trouve que vous pouvez écrire des applications à grande échelle en utilisant des objets immuables pour la majorité du système sans trop de douleur.
vous avez à peu près répondu à votre propre question. La spécification JavaBean, Je ne crois pas, mentionne quoi que ce soit au sujet de l'immutabilité, pourtant les JavaBeans sont le pain et le beurre de beaucoup de cadres Java.
fondamentalement, dans le monde réel, l'état associé à de nombreuses identités particulières va changer. Si je demande Quelle est "la position actuelle de la Buick de Joe", aujourd'hui il pourrait être un endroit à Seattle, et demain il pourrait être un endroit à Los Alamos. Il serait possible de définir et de créer un objet GeographicLocation
dont la valeur représentera toujours L'endroit où se trouvait la Buick de Joe à un moment précis dans le temps et ne changera jamais--si aujourd'hui il représente un endroit à Seattle, ensuite, il sera toujours faire. Un tel objet, cependant, n'ont aucune identité comme "l'emplacement de Joe la Buick".
il peut aussi être possible de définir des choses de sorte qu'il y ait un objet VehicleLocation
qui est connecté à la Buick de Joe de sorte que l'objet représente toujours"l'emplacement actuel de la Buick de Joe". Un tel objet pourrait conserver son identité comme "l'emplacement actuel de la Buick de Joe", même si la voiture se déplace, mais ne représenterait pas une constante l'emplacement géographique. Définir " l'identité "peut être délicat si l'on considère le scénario où Joe vend sa Buick à Bob et achète une Ford-si la piste objet" l'emplacement actuel de la Ford de Joe "ou"l'emplacement actuel de la Buick de Bob" - mais dans de nombreux cas, ces problèmes peuvent être évités en utilisant un modèle de données qui garantit que certains aspects de l'identité de L'objet ne changera jamais.
il n'est pas possible que tout ce qui concerne un objet soit immuable. Si un objet est immuable, alors il ne peut pas avoir une identité immuable qui encapsule quoi que ce soit au-delà de son état actuel. Si un objet est mutable, cependant, il peut avoir une identité immuable dont le sens transcende son état actuel. Dans de nombreuses situations, avoir une identité immuable est plus utile que d'avoir un État immuable, et dans de telles situations, les objets mutables sont presque essentiels. Alors qu'il est possible dans certains cas de "simuler" des objets mutables en ayant un objet immuable qui chercherait grâce à la version la plus récente d'un objet immuable pour trouver des informations qui peuvent "changer" entre une version et la suivante, de telles approches sont souvent extrêmement inefficaces. Même si on pouvait par magie recevoir une fois par minute un livre relié qui donnait l'emplacement de chaque véhicule partout, regarder "la Buick de Joe" dans le livre prendrait beaucoup plus de temps que simplement demander un "emplacement actuel de la Buick de Joe" objet qui aurait toujours savoir où la voiture était.
le concept de types immuables est assez rare pour les gens habitués à des styles de programmation impératifs. Cependant, pour de nombreuses situations immutabilité a de sérieux avantages, vous avez déjà nommé les plus importants.
il existe de bonnes façons de mettre en œuvre des arbres, des files d'attente, des piles, des dequeues et d'autres structures de données équilibrées immuables. Et en fait, de nombreux langages / cadres de programmation modernes ne prennent en charge que des chaînes immuables en raison de leurs avantages et parfois aussi d'autres objets.
Avec un objet immuable, si la valeur doit être modifiée, elle doit être remplacée par une nouvelle instance. Selon le cycle de vie de l'objet, le remplacer par une instance différente peut augmenter le temps de collecte des déchets. Cela devient plus critique si l'objet est gardé en mémoire assez longtemps pour être placé dans la génération tenure.
le problème en java est que l'on doit vivre avec tous ces objets, où la classe ressemble à:
class Mutable {
State1 f1;
MoreState f2;
void doSomething() { // mutate the state, but don't document it }
void doSomethingElse() /// mutate the state heavily, do not mention in doc
}
(noter l'interface clonable manquante).
le problème avec le collecteur d'ordures n'est pas si grand de nos jours. Les VM sont heureux avec les petits objets vivants.
les progrès de la technologie de compilation/JIT permettront, tôt ou tard, d'optimiser la création temporaire d'objets intermédiaires. Par exemple:
BigInteger three =, two =, i1 = ...;
BigInteger i2 = i1.mul(three).div(two);
le JIT pourrait remarquer que l'objet intermédiaire i1.mul(trois) peut être utilisé pour le résultat final et appeler une variante de la méthode div qui fonctionne sur un accumulateur mutable.
Voir Fonctionnel Java pour atteindre une réponse complète à votre question.
immutabilité, comme tout autre motif de conception, ne doit être utilisé que lorsque vous en avez besoin. Vous donnez l'exemple de la sécurité du filetage: dans une application très filetée, vous pouvez préférer l'immutabilité à la dépense supplémentaire de rendre vous-même le filetage sûr. Cependant, si votre dessin exige que les objets soient mutables, ne faites pas tout pour les rendre immuables, simplement parce que "c'est un dessin".
quant à votre graphe, vous pouvez choisir de rendre vos noeuds immuables et laissez une autre classe s'occuper des connexions entre eux, ou vous pourriez faire un noeud mutable qui prend soin de ses propres enfants et a une classe de valeur immuable.
probablement le plus gros coût de L'utilisation d'objets immutables en Java est que les futurs développeurs ne s'y attendront pas ou ne seront pas habitués à ce style. Attendez-vous à documenter lourdement ou à regarder beaucoup de vos objets engendrer des pairs mutables au fil du temps.
cela dit, la seule vraie raison technique à laquelle je peux penser pour éviter les objets immuables est GC churn. Pour la plupart des applications, Je ne pense pas que ce soit une raison impérieuse de les éviter.
la plus grande chose Je n'ai jamais fait avec un ~90% immuable objets était un jeu d'interpréteur scheme-esque, donc il est certainement possible de faire des projets Java complexes.
dans les données immuables vous ne définissez pas les choses deux fois... voir Haskell et scala vals (et clojure of cource)...
par exemple.. pour une structure de données.. comme un arbre, lorsque vous effectuez une opération d'écriture à l'arbre, en fait, vous ajoutez des éléments à l'extérieur de l'immuable arbre.. après que vous avez fait.. l'arbre et la branche sont recombinés dans une nouvelle arborescence.. ainsi, comme ceci, vous pourriez effectuer des lectures simultanées et écrit très safelly..
dans le modèle traditionnel, vous devez verrouiller une valeur car elle peut être réacheminée à tout moment.. si.. on finit avec une zone très chaude pour les fils..puisqu'ils agissent séquentiellement là de toute façon..
avec les données imuttable, vous ne définissez pas les choses plus d'une fois.. c'est une toute nouvelle façon de programmer.. vous pouvez vous retrouver avec un peu plus de mémoire.. mais la parallélisation est naturel et indolore..
Comme avec n'importe quel outil, vous devez savoir quand l'utiliser et quand ne pas.
Comme Tehblanx rappelle que si vous souhaitez modifier l'état d'une variable qui contient un objet immuable, vous devez créer un nouvel objet, qui peut être coûteux, surtout si l'objet est gros et complexe. Absolument vrai, mais cela signifie simplement que vous devez décider intelligemment quels objets devraient être mutables et lesquels devraient être immuables. Si quelqu'un est en train de dire que TOUS les objets ça devrait être immuable, c'est juste des paroles folles.
j'ai tendance à dire que les objets qui représentent un seul" fait " logique devraient être immuables, tandis que les objets qui représentent plusieurs faits devraient être mutables. Comme, un entier ou une chaîne de caractères devrait être immuable. Un objet "client" qui contient le nom, l'adresse, le montant actuel, la date du dernier achat, etc. devrait être mutable. Bien sûr, je peux immédiatement penser à une centaine d'exceptions à cette règle générale. Une exception je fais tout le temps est où j'ai une classe qui existe comme un emballage pour contenir une primitive dans certains cas où une primitive n'est pas légale, comme dans une collection, mais j'ai besoin de la mettre à jour constamment.
en Java, une méthode ne peut pas retourner plusieurs objets, comme return a, b, c
. Retourner un tableau d'objets rend le code moche. Dans cette situation, je dois passer des objets mutables à la méthode et la laisser changer les états de ces objets. Cependant, je ne sais pas si retourner plusieurs objets est une odeur de code ou non.
la réponse est zéro. Il n'y a de bonnes raisons d'être mutable.
Vous avez des problèmes avec beaucoup de cadres(ou versions framework) qui nécessitent mutable objets afin de travailler avec eux(Printemps, je suis flagrante dans votre direction). Alors que vous travaillez avec eux et pêchez à travers le code, vous agiterez votre poing dans la colère que vous avez besoin d'introduire la mutabilité sale dans un bloc autrement glorieux de code quand il aurait pu être facilement évité.
je suis sûr qu'il y a des cas de coin limités(probablement plus hypothétique que quoi que ce soit) où les frais généraux de la création et de la collecte d'objet est unceptable. Mais je presse les gens qui feraient cet argument de regarder les langues comme le scala où les collections incluses sont immuables par défaut et puis regarder la multitude d'applications critiques de performance construites sur le dessus de ce concept.
c'est bien sûr une hyperbole. En réalité, vous devriez aller avec immutabilité d'abord, voir si elle vous cause n'importe quels mesurables problèmes, si elle introduit alors la mutabilité, mais assurez-vous que vous pouvez prouver qu'il résout votre problème. Sinon, vous venez de créer une responsabilité sans bénéfice. Ce faisant, je pense que vous trouverez des cas objectifs de "complexité de la mise en œuvre" et de "rigidité" très difficiles à rendre.
certaines implémentations d'objets immuables ont des moyens transactionnels pour mettre à jour un objet immuable. Similaire à la façon dont les bases de données fournissent des propagations et des retours en arrière sûrs. Mais en contraste apparent avec beaucoup de réponses ici. Les objets immuables ne sont jamais changés. Une opération typique serait.
B = append(A,C)
B est un nouvel objet. Tout comme A et C. Aucune modification n'a été faite à A ou C. À L'interne, une implémentation de l'arbre noir rouge rend une telle sémantique assez rapide pour être utilisable.
L'inconvénient est qu'il n'est pas aussi rapide que de faire les activités en place. Mais qui compare uniquement une seule partie du système. Lorsque nous évaluons les inconvénients possibles, nous devons considérer le système dans son ensemble. Et personnellement, je n'ai pas une image claire de l'impact total. Bien que je soupçonne immutabilité gagne à la fin.
je sais que certains experts soutiennent qu'il y a des disputes au niveau supérieur de l'arbre noir rouge. Et c'est négatif effet dans à travers.
mon plus grand souci avec les structures de données immuables est comment les sauver / les reconstituer. C'est-à-dire, si une classe a des champs finaux, Je ne peux pas l'instancier et puis définir ses champs.