Comment puis-je identifier les objets immuables en Java
Dans mon code, je suis entrain de créer une collection d'objets qui seront accessibles par différents threads dans un mode qui n'est sûr que si les objets sont immuables. Lors d'une tentative d'insertion d'un nouvel objet dans ma collection, je veux tester pour voir si elle est immuable (si pas, je vais jeter une exception).
Seule chose que je peux faire est de vérifier quelques-uns bien connus immuable types:
private static final Set<Class> knownImmutables = new HashSet<Class>(Arrays.asList(
String.class, Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class
));
...
public static boolean isImmutable(Object o) {
return knownImmutables.contains(o.getClass());
}
Cela me fait 90% du chemin, mais parfois, certains de mes utilisateurs voudront créer de simples immuable types de leur propre:
public class ImmutableRectangle {
private final int width;
private final int height;
public ImmutableRectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() { return width; }
public int getHeight() { return height; }
}
Est-il un moyen (peut-être l'aide de la réflexion) que j'ai pu détecter de manière fiable si une classe est immuable? Les faux positifs (pensant que c'est immuable, quand ce n'est pas le cas) ne sont pas acceptables, mais des faux négatifs (pensant que c'est mutable, quand ce n'est pas le cas) sont.
Modifiées afin d'ajouter: Merci pour les remarques pertinentes et des réponses utiles. Comme certaines des réponses souligné, j'ai négligé de définir mes objectifs de sécurité. La menace est ici désemparés développeurs -- c'est un morceau de code de la structure qui sera utilisé par un grand nombre de personnes qui ne savons presque rien sur le filetage et ne sera pas la lecture de la documentation. Je n'ai PAS besoin pour se défendre contre les logiciels malveillants -- n'importe qui assez intelligent pour muter une Chaîne de caractères ou d'effectuer d'autres manigances sera également assez intelligent pour savoir qu'il n'est pas sûr dans ce cas. L'analyse statique du code EST une option, tant qu'il est automatisé, mais les révisions de code ne peut pas compter sur lui car il est impossible de garantir à chaque examen aura threading-savvy examinateurs.
Vous devez vous connecter pour publier un commentaire.
Il n'y a aucun moyen fiable de détecter si une classe est immuable. C'est parce qu'il y a de nombreuses façons d'une propriété d'une classe peut être modifié et vous ne pouvez pas détecter tous les d'eux par le biais de la réflexion.
Le seul moyen de se rapprocher de ce qui est:
Ensuite, vous pouvez vérifier avec le code suivant si l'objet que vous avez est immuable:
Mise à jour: Comme suggéré dans les commentaires, il pourrait être étendu à recurse sur la super-classe au lieu de vérifier, pour une certaine classe. Il a également été suggéré d'utiliser de manière récursive isImmutable dans le isValidFieldType Méthode. Cela pourrait probablement travailler et j'ai aussi fait quelques tests. Mais ce n'est pas anodin. Vous ne pouvez pas il suffit de cocher tous les types de champ avec un appel à isImmutable, car les chaînes de caractères déjà échoue à ce test (son champ
hash
n'est pas définitif!). Aussi, vous êtes facilement en cours d'exécution dans d'interminables récurrences, provoquant StackOverflowErrors 😉 d'Autres problèmes peuvent être causés par des médicaments génériques, où vous devez également vérifier leurs types pour immutablity.Je pense qu'avec un peu de travail, le potentiel de ces problèmes peuvent être résolus en quelque sorte. Mais alors, vous devez vous demander d'abord si elle vaut vraiment la peine (c'est aussi la performance sage).
Utiliser le Immuable d'annotation à partir d' Java de la Simultanéité dans la Pratique. L'outil FindBugs peut alors aider à détecter des classes qui sont mutables, mais ne devrait pas l'être.
Dans mon entreprise nous avons défini un Attribut appelé
@Immutable
. Si vous choisissez de vous joindre à une classe, cela signifie que vous promettons que vous êtes immuable.Il travaille pour la documentation, et dans votre cas, il fonctionne comme un filtre.
Bien sûr, vous êtes toujours selon l'auteur de garder sa parole au sujet d'être immuable, mais puisque l'auteur a ajouté l'annotation, il est une hypothèse raisonnable.
Fondamentalement pas.
Vous pourriez construire un géant blanc-liste des accepté les classes, mais je pense que le moins fou serait de l'écrire dans les documents de la collection de tout ce qui se passe est cette collection doit être immuable.
Edit: d'Autres personnes ont suggéré de faire un immuable annotation. C'est très bien, mais vous avez besoin de la documentation. Sinon les gens vont juste penser "si j'ai mis cette annotation sur ma classe que je peux stocker dans la collection" et viens de mandrin sur quoi que ce soit, immuable et changeante des classes semblables. En fait, je me méfie d'avoir un immuable annotation juste au cas où les gens pensent que l'annotation fait leur classe immuable.
Cela pourrait être un autre indice:
Si la classe n'a pas de poseurs alors il ne peut pas être muté, accordé paramètres, il a été créé avec sont soit des "primitifs" types ou non mutable eux-mêmes.
Aussi pas de méthodes pourraient être surchargée, tous les champs sont finales et privé,
Je vais essayer de coder quelque chose demain pour vous, mais Simon du code à l'aide de la réflexion semble assez bon.
Dans le temps, essayez de récupérer une copie de la "Effective Java" livre par Josh Bloc , il a un Article relatif à ce sujet. Tout est de ne pas vous dire comment détecter un inmmutable classe, il montre comment créer un bon.
L'élément est appelé: "Favoriser l'immutabilité"
lien:
http://java.sun.com/docs/books/effective/
Pas une réponse directe à votre question, mais gardez à l'esprit que les objets qui sont immuables ne sont pas automatiquement la garantie d'être thread-safe (malheureusement). Le Code doit être sans effets secondaires pour être thread-safe, et c'est un peu plus difficile.
Supposons que vous avez cette classe:
Puis le
foolAround()
méthode pourrait comporter certains non thread-safe opérations, ce qui va souffler votre application. Et il n'est pas possible de le tester à l'aide de la réflexion, comme la référence ne peut être trouvée dans le corps de la méthode, pas dans les champs ou dans les exposés de l'interface.Autre que cela, les autres sont correctes: vous pouvez numériser pour déclarer tous les champs de la classe, vérifier si chacun d'entre eux est finale et aussi immuable de la classe, et vous avez terminé. Je ne pense pas que les méthodes de finale est une exigence.
Aussi, être prudent sur la façon récursive la vérification des champs dépendants de l'immutabilité, vous risquez de vous retrouver avec des cercles:
Classes A et B sont parfaitement immuable (et peut-être même utilisable à travers une réflexion hacks), mais naïf récursive code dans une boucle sans fin de la vérification A, puis B, puis de nouveau, à partir de B, ...
Vous pouvez corriger cela avec une " vue " de la carte qui n'autorise pas les cycles, ou avec certains vraiment intelligent de code qui décide de classes sont immuables si tous leurs dependees sont immuables qu'en fonction d'eux-mêmes, mais qui va être vraiment compliqué...
Vous Pouvez Demander à vos clients d'ajouter des métadonnées (annotations) et de les vérifier au moment de l'exécution, de réflexion, comme ceci:
Métadonnées:
Code Client:
Puis par l'aide de la réflexion sur la classe, vérifier si elle a l'annotation (je voudrais coller le code, mais son passe-partout et peuvent être trouvés facilement en ligne)
pourquoi toutes ces recommandations exigent la classe finale? si vous utilisez de la réflexion afin de vérifier la classe de chaque objet, et vous pouvez déterminer par programmation que cette classe est immuable (immuable, définitive champs), alors vous n'avez pas besoin d'exiger que la classe elle-même est définitive.
Vous pouvez utiliser des AOP et
@Immuable
d'annotation à partir d' jcabi-aspects:Comme les autres answerers déjà dit, à mon humble avis il n'y a aucun moyen fiable de savoir si un objet est vraiment immuable.
Je voudrais simplement présenter une interface "Immuable" à vérifier lors de l'ajout. Cela fonctionne comme un indice que seuls des objets immuables doit être inséré pour quelque raison que ce soit vous le faites.
Essayez ceci:
class MyNode { MyNode parent; public MyNode() {} public MyNode(MyNode parent) { this.parent = parent; } }
) : vous obtiendrez unStackOverflowError
.BigInteger
s'étendNumber
et la javadoc déclare qu'il est immuable la classe, mais ni leBigInteger
classe, ni ses méthodes sontfinal
, de sorte que la classe peut être surchargée et de la classe enfant peut changer laBigInteger
comportement de la classe.À ma connaissance, il n'y a aucun moyen d'identifier des objets immuables qui est correct à 100%. Cependant, j'ai écrit une bibliothèque pour vous rapprocher. Il effectue l'analyse de bytecode d'une classe afin de déterminer si elle est immuable ou non, et peuvent s'exécuter lors de l'exécution. Il est à la stricte côté, il permet également une liste blanche connu immuable classes.
Vous pouvez le vérifier sur: http://www.mutabilitydetector.org
Il vous permet d'écrire ce type de code dans votre application:
Il est gratuit et open source sous licence Apache 2.0.
Découvrez joe-e, une mise en œuvre de capacités pour java.
Quelque chose qui fonctionne pour un pourcentage élevé de builtin classes, c'est de tester pour instanceof Comparables. Pour les classes qui ne sont pas immuables comme la Date, ils sont souvent traités comme immuable dans la plupart des cas.
J'apprécie et j'admire la quantité de travail Grundlefleck a mis dans sa mutabilité détecteur, mais je pense que c'est un peu exagéré. Vous pouvez écrire un simple, mais en pratique très adéquate (c'est pragmatique) détecteur comme suit:
(note: ceci est une copie de mon commentaire ici: https://stackoverflow.com/a/28111150/773113)
Tout d'abord, vous n'allez pas être juste écrire une méthode qui détermine si une classe est immuable; au lieu de cela, vous aurez besoin d'écrire une immutabilité détecteur de classe, parce qu'il va avoir à maintenir un certain état. L'état du détecteur seront détectés immutabilité de toutes les classes dont il a examiné jusqu'à présent. Ce n'est pas seulement utile pour les performances, mais il est en fait nécessaire, car une classe peut contenir une référence circulaire, qui serait la cause d'une simpliste immutabilité détecteur de tomber dans une récursion infinie.
L'immutabilité d'une classe a quatre valeurs possibles:
Unknown
,Mutable
,Immutable
, etCalculating
. Vous aurez probablement envie d'avoir une carte qui associe à chaque classe que vous avez rencontrés jusqu'à présent à l'immutabilité de la valeur. Bien sûr,Unknown
n'a pas réellement besoin d'être mis en œuvre, puisqu'il sera l'état implicite de toute catégorie qui n'est pas encore à la carte.Ainsi, lorsque vous commencez à l'examen d'une classe, vous l'associez à un
Calculating
valeur dans la carte, et lorsque vous avez terminé, vous remplacezCalculating
soit avecImmutable
ouMutable
.Pour chaque classe, vous avez uniquement besoin de vérifier le champ de membres, pas le code. L'idée de la vérification de bytecode est plutôt mal venu.
Tout d'abord, vous devez pas vérifier si une classe est définitive, La finalité d'une classe n'a pas d'incidence sur son immuabilité. Au lieu de cela, une méthode qui attend un immuable paramètre doit tout d'abord invoquer l'immutabilité détecteur d'affirmer l'immutabilité de la classe de l'objet réel, qui a été adoptée. Ce test peut être omis si le type du paramètre est une classe finale, de sorte que la finalité est bon pour les performances, mais pas strictement nécessaire. Aussi, comme vous le verrez plus bas, un champ dont le type est d'un non-classe finale sera la cause de la classe de déclaration, pour être considéré comme mutables, mais encore, c'est un problème de la classe de déclaration, pas le problème de la non-final immuable membre de la classe. Il est parfaitement acceptable d'avoir une haute hiérarchie de l'immuable classes, dans lequel tous les nœuds non feuille doit bien sûr être non-final.
Vous devriez pas vérifier si un champ est privé; il est parfaitement bien pour une classe à avoir un champ public, et la visibilité du champ n'a pas d'incidence sur l'immutabilité de la classe de déclaration, en aucune manière, la forme ou la forme. Vous avez uniquement besoin de vérifier si le champ est définitive et son type est immuable.
Lors de l'examen d'une classe, ce que vous voulez faire tout d'abord, de manière récursive pour déterminer l'immutabilité de sa
super
classe. Si le super est mutable, puis le descendant est par définition mutable trop.Ensuite, vous avez uniquement besoin de vérifier la déclaré champs de la classe, pas tous champs.
Si un champ n'est pas final, votre classe est mutable.
Si un champ est définitif, mais le type du champ est mutable, votre classe est mutable. (Les tableaux sont, par définition, mutable.)
Si un champ est définitive, et le type du champ est
Calculating
, puis de les ignorer et de passer à la zone suivante. Si tous les champs sont immuables ouCalculating
, votre classe est immuable.Si le type du champ est une interface ou une classe abstraite, ou un non-finale de la classe, alors il est considéré comme mutables, puisque vous n'avez absolument aucun contrôle sur ce que la mise en œuvre effective peut faire. Cela peut sembler comme un problème insurmontable, car cela signifie que l'enveloppant d'un modifiable collection à l'intérieur d'un
UnmodifiableCollection
échouera toujours l'immutabilité de test, mais il est en fait très bien, et il peut être manipulé avec la solution de contournement suivante.Certaines classes peuvent contenir des non-final champs et encore efficacement immuable. Un exemple de ceci est le
String
classe. D'autres classes qui entrent dans cette catégorie sont des classes qui contiennent des non-finale membres purement à des fins de contrôle des performances (invocation des comptoirs, etc.), les classes qui implémentent de popsicle immutabilité (regarder), et des classes qui contiennent des membres qui sont des interfaces qui sont connus pour ne pas causer des effets secondaires. Aussi, si une classe contient de véritables champs mutables, mais il promet de ne pas les prendre en compte lors du calcul de hashCode() et equals(), puis la classe est bien sûr dangereux quand il s'agit de multi-threading, mais il peut encore être considérée comme immuable pour le but de l'utiliser comme une clé dans une carte. Donc, tous ces cas peuvent être traités de deux façons:D'ajouter manuellement les classes et interfaces) pour votre immutabilité du détecteur. Si vous savez qu'une certaine classe est effectivement immuable malgré le fait que l'immutabilité de test pour qu'il échoue, vous pouvez manuellement ajouter une entrée à votre détecteur qui associe avec
Immutable
. De cette façon, le détecteur ne tentera jamais de vérifier si elle est immuable, il sera toujours juste de dire " oui, il est.'L'introduction d'un
@ImmutabilityOverride
annotation. Votre immutabilité détecteur permet de vérifier la présence de cette annotation sur un champ, et s'il est présent, il peut considérer le champ comme immuable, malgré le fait que le champ peut être non-final ou son type peut être mutables. Le détecteur peut également vérifier la présence de cette annotation sur la classe, donc le traitement de la classe comme immuable, sans même prendre la peine de vérifier ses champs.J'espère que cela aide les générations futures.